home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / OGRID110 / GLTSHEET.PAS < prev    next >
Pascal/Delphi Source File  |  1995-06-01  |  173KB  |  5,258 lines

  1. {********************************************************************
  2.  
  3.   OOGrid Library(TM) for Borland/Turbo Pascal (Real Mode/TV)
  4.   Copyright (C) 1994, 1995 by Arturo J. Monge
  5.   Portions Copyright (C) 1989,1990 Borland International, Inc.
  6.  
  7.   OOGrid Library(TM) Main Unit:
  8.     Implementation of a spreadsheet.
  9.  
  10.   Copyright (C) 1994, 1995 by Arturo J. Monge
  11.  
  12.   Last Modification : June 1st, 1995
  13.  
  14. *********************************************************************}
  15.  
  16. {$F+,O+,N+,E+,X+,V-}
  17.  
  18. unit GLTSheet;
  19.  
  20. {****************************************************************************}
  21.                                  interface
  22. {****************************************************************************}
  23.  
  24. uses Crt, Dialogs, Dos, Objects, Views, Drivers, TCHash, GLSort, GLParser, GLSupprt,
  25.      GLCell, GLViews, GLEquate;
  26.  
  27. const
  28.  
  29. { Constants used by TSpreadSheet's methods }
  30.  
  31.   RedrawYes = True;
  32.   RedrawNo  = False;
  33.   EditYes = True;
  34.   EditNo = False;
  35.   DisplayYes = True;
  36.   DisplayNo = False;
  37.   ModifiedYes = True;
  38.   ModifiedNo = False;
  39.   RemoveBlock = True;
  40.   RemoveSingleCell = False;
  41.   ChangeYes = True;
  42.   ChangeNo = False;
  43.  
  44. const
  45.  
  46. { TSpreadSheet palette }
  47.  
  48.  CSpreadSheet = #12#13#14#15#16#17#18#19#20#21#22#23#24#25#26#27#28#29#30+
  49.                 #31#32#33#34#35#36;
  50.  
  51. { CSpreadSheet palette layout }
  52.  
  53.   { 1 = Empty Cell }
  54.   { 2 = Value Cell }
  55.   { 3 = Text Cell }
  56.   { 4 = Repeat Cell }
  57.   { 5 = Formula Cell }
  58.   { 6 = Column headers }
  59.   { 7 = Row numbers }
  60.   { 8 = Cell Data Area }
  61.   { 9 = Cell Contents Area }
  62.   { 10 = Spreadsheet Info Area }
  63.   { 11 = Cell In Block }
  64.   { 12 = Cell Highlighted }
  65.   { 13 = Cell Highlighted in Block }
  66.   { 14 = Unlocked Cell }
  67.   { 15 = Unlocked Cell in Block }
  68.   { 16 = Unlocked Cell Highlighted }
  69.   { 17 = Unlocked Cell Highlighted in Block }
  70.   { 18 = Cell Error }
  71.   { 19 = Cell Error in Block }
  72.   { 20 = Cell Error Highlighted }
  73.   { 21 = Cell Error Highlighted in Block }
  74.   { 22 = Unlocked Cell Error }
  75.   { 23 = Unlocked Cell Error in Block }
  76.   { 24 = Unlocked Cell Error Highlighted }
  77.   { 25 = Unlocked Cell Error Highlighted in Block }
  78.  
  79. const
  80.   DisplayEnabled : Boolean = True;
  81.   { Used by TSpreadSheet's SetChanged method.  When DisplayEnabled is True,
  82.     SetChanged will display the information area of the spreadsheet
  83.     to indicate a change in the Modified state.  This global constant
  84.     was added to be able to store TSpreadSheet objects in a resource
  85.     file, without having to insert them in the application first.  This
  86.     field is always set to False when using the GLTVR_US or GLTVR_SP
  87.     units. }
  88.   {#X TSpreadSheet.DisplayInfo TSpreadSheet.SetChanged TSpreadSheet.Modified }
  89.  
  90. type
  91.    PColStart = ^ColStartArray;
  92.    ColStartArray = array[0..ScreenCols] of Byte;
  93.    { Array used to store the screen positions where displayed columns start }
  94.  
  95.    PSpreadSheet = ^TSpreadSheet;
  96.    TSpreadSheet = object(TScroller)
  97.          Modified             : Boolean;
  98.          MaxDecimalPlaces     : Byte;
  99.          DefaultColWidth      : Byte;
  100.          DefaultDecimalPlaces : Byte;
  101.          DefaultCurrency      : CurrencyStr;
  102.          MaxRows              : Integer;
  103.          MaxCols              : Integer;
  104.          MaxColWidth          : Byte;
  105.          MaxScreenCols        : Byte;
  106.          TotalRows            : ScreenRowRange;
  107.          RowNumberSpace       : Byte;
  108.          OldCurrPos           : CellPos;
  109.          CurrPos              : CellPos;
  110.          LastPos              : CellPos;
  111.          ScreenBlock          : PBlock;
  112.          CurrBlock            : PBlock;
  113.          BlockOn              : Boolean;
  114.          ColArea              : TScreenArea;
  115.          RowArea              : TScreenArea;
  116.          InfoArea             : TScreenArea;
  117.          DataArea             : TScreenArea;
  118.          DisplayArea          : TScreenArea;
  119.          ContentsArea         : TScreenArea;
  120.          BlankArea            : TScreenArea;
  121.          NoBlankArea          : Boolean;
  122.          ColStart             : PColStart;
  123.          CellHash             : TCellHashTable;
  124.          WidthHash            : TWidthHashTable;
  125.          OverwriteHash        : TOverwriteHashTable;
  126.          FormatHash           : TFormatHashTable;
  127.          DisplayFormulas      : Boolean;
  128.          AutoCalc             : Boolean;
  129.          GoToEnd              : Boolean;
  130.          KeyPressed           : Boolean;
  131.          EmptyRowsAtTop       : Byte;
  132.          EmptyRowsAtBottom    : Byte;
  133.          SheetProtected       : Boolean;
  134.          DisplayHeaders       : Boolean;
  135.          UnlockedHash         : TUnlockedHashTable;
  136.          ColHeadersHash       : THeadersHashTable;
  137.          MessageDialog : PDialog;
  138.          { This field is used as a pointer the currently active message
  139.            dialog.  If no message dialogs are active, TempDialog is set
  140.            to nil.
  141.  
  142.            Message dialogs are modeless dialogs, with no buttons, which
  143.            only purpose is giving the user a message while TSpreadSheet
  144.            is working on a given operation.  For example, }
  145.          {#F+}
  146.          {}
  147.          {   'Updating data tables... please wait.' }
  148.          {#F-}
  149.       constructor Init(var Bounds: TRect; InitCells: LongInt;
  150.                            AEmptyRowsAtTop, AEmptyRowsAtBottom: Byte;
  151.                            AHScrollBar, AVScrollBar: PScrollBar;
  152.                            AInitMaxCols, AInitMaxRows: Integer;
  153.                            InitDefaultColWidth,
  154.                            InitDefaultDecimalPlaces,
  155.                            InitMaxDecimalPlaces: Byte;
  156.                            InitDefaultCurrency: CurrencyStr);
  157.       function  AddCell(CellType: CellTypes; Pos: CellPos; Error: Boolean;
  158.                   Value: Extended; Input: String): Boolean; virtual;
  159.       function  CellHashStart(TotalCells: LongInt): BucketRange; virtual;
  160.       function  CellsProtected(Block: TBlock): Boolean; virtual;
  161.       function  CellToFString(P: CellPos; var AColor: Byte): String; virtual;
  162.       procedure ChangeBounds(var Bounds: TRect); virtual;
  163.       procedure ChangeColHeaders; virtual;
  164.       procedure ChangeColWidth; virtual;
  165.       procedure ChangeHeader(Block: PBlock; AColumn: Word; NewHeader: String);
  166.         virtual;
  167.       { Changes the header of a column or block of columns.
  168.  
  169.         The Block and AColumn parameters are mutually exclusive.  If Block
  170.         is not nil, the AColumn parameter will be ignored; otherwise, the
  171.         Block parameter is ignored and the AColumn parameter is used instead.
  172.  
  173.         Use Block if you want to change the header of a block of
  174.         columns.  Use AColumn if you want to change the header of a single
  175.         column. }
  176.       procedure ChangeWidth(Block: PBlock; AColumn: Word; NewWidth: Byte);
  177.         virtual;
  178.       { Changes the width of a column or block of columns.
  179.  
  180.         The Block and AColumn parameters are mutually exclusive.  If Block
  181.         is not nil, the AColumn parameter will be ignored; otherwise, the
  182.         Block parameter is ignored and the AColumn parameter is used instead.
  183.  
  184.         Use Block if you want to change the width of a block of
  185.         columns.  Use AColumn if you want to change the width of a single
  186.         column. }
  187.       procedure CheckForDragging; virtual;
  188.       procedure ClearCurrBlock; virtual;
  189.       procedure ClearScreenArea(AreaToClear: PScreenArea); virtual;
  190.       function  ColHeadersHashStart : BucketRange; virtual;
  191.       function  ColumnToString(Column: Word): String; virtual;
  192.       function  ColToX(Col: Integer): Byte; virtual;
  193.       function  ColWidth(Col: Integer): Byte; virtual;
  194.       procedure CopyCellBlock; virtual;
  195.       procedure DeleteBlock(Block: TBlock; var Deleted: Boolean); virtual;
  196.       procedure DeleteCell(Pos: CellPos; var Deleted: Boolean); virtual;
  197.       procedure DeleteColFromHash(Block: TBlock; Columns, EndDelCol: Word);
  198.         virtual;
  199.       procedure DeleteColHeaders(Block: PBlock); virtual;
  200.       procedure DeleteColumns; virtual;
  201.       procedure DeleteRowFromHash(Block: TBlock; Rows, EndDelRow: Word);
  202.         virtual;
  203.       procedure DeleteRows; virtual;
  204.       procedure DisplayAllCells; virtual;
  205.       procedure DisplayBlock(B: TBlock); virtual;
  206.       procedure DisplayBlockDiff(B1, B2: TBlock); virtual;
  207.       procedure DisplayCell(P: CellPos); virtual;
  208.       procedure DisplayCellBlock(C1, R1, C2, R2: Word); virtual;
  209.       procedure DisplayCellData; virtual;
  210.       procedure DisplayCols; virtual;
  211.       procedure DisplayInfo; virtual;
  212.       procedure DisplayRows; virtual;
  213.       procedure DoAfterAddingCell; virtual;
  214.       function  DoBeforeAddingCell: Boolean; virtual;
  215.       { This function is called immediatly after an input string has been
  216.         parsed in the #HandleInput# method, and before actually adding the
  217.         corresponding cell.  If DoBeforeAddingCell returns TRUE the cell is
  218.         added; if it returns FALSE, the cell won't be added and the user
  219.         will be returned to the input line.
  220.  
  221.         If there is an error in the input string, this function will not
  222.         be called.
  223.  
  224.         You should override this function if you want, for example, to
  225.         validate the data that is entered in each column.  If the data
  226.         entered by the user cannot be added to the current column,
  227.         DoBeforeAddingCell can display an error message and return false.
  228.         By default, DoBeforeAddingCell returns true. }
  229.       procedure DragCursorWithMouse(Event: TEvent); virtual;
  230.       procedure Draw; virtual;
  231.       procedure EraseCellBlock(EraseBlock: Boolean); virtual;
  232.       procedure ExtendCurrBlock(Redraw : Boolean); virtual;
  233.       procedure FindLastPos(DPos: CellPos); virtual;
  234.       procedure FindScreenColStart; virtual;
  235.       procedure FindScreenColStop; virtual;
  236.       procedure FindScreenRowStart; virtual;
  237.       procedure FindScreenRowStop; virtual;
  238.       procedure FixBlockOverWrite(Block: TBlock); virtual;
  239.       function  FixOverWrite: Boolean; virtual;
  240.       procedure FormatDefault; virtual;
  241.       function  FStringSituationColor(P: CellPos; var CP: PCell;
  242.                   var HasError, ColorFound: Boolean): Byte; virtual;
  243.       procedure FormatCells; virtual;
  244.       function GetNumber: Integer;
  245.       { Returns the number of the window that owns the object }
  246.       function  GetPalette: PPalette; virtual;
  247.       procedure GoToCell; virtual;
  248.       procedure GoToPos(Pos: CellPos); virtual;
  249.       { Moves the cursor to the given position and redraws the screen if
  250.         necessary. }
  251.       procedure HandleEvent(var Event: TEvent); virtual;
  252.       procedure HandleInput(FirstChar: String; Editing: Boolean); virtual;
  253.       procedure InitCurrPos; virtual;
  254.       procedure InsertColToHash(Block: TBlock; Columns, StartInsCol: Word);
  255.         virtual;
  256.       procedure InsertColumns; virtual;
  257.       procedure InsertRowToHash(Block: TBlock; Rows, StartInsRow: Word);
  258.         virtual;
  259.       procedure InsertRows; virtual;
  260.       constructor Load(var S: TStream);
  261.       procedure LoadDelimited(FileName: PathStr); virtual;
  262.       { This method imports a comma delimited file of a certain format and
  263.         is intended only as an example of how to import comma delimited files.
  264.         This method must be overridden if you wish to import delimited files
  265.         of different formats }
  266.       procedure LoadHashTables(var S: TStream; AdjustAfter: CellPos;
  267.                   RowAdjustment, ColAdjustment: Integer); virtual;
  268.       procedure LoadTablesFromTempFile(AdjustAfter: CellPos;
  269.                   RowAdjustment, ColAdjustment: Integer); virtual;
  270.       procedure LocateCursorWithMouse(Event: TEvent); virtual;
  271.       procedure MoveCell(OldPos: CellPos); virtual;
  272.       procedure MoveCellBlock; virtual;
  273.       procedure MoveDown; virtual;
  274.       procedure MoveHome; virtual;
  275.       procedure MoveLeft; virtual;
  276.       procedure MovePgDown; virtual;
  277.       procedure MovePgLeft; virtual;
  278.       procedure MovePgRight; virtual;
  279.       procedure MovePgUp; virtual;
  280.       procedure MoveRight; virtual;
  281.       procedure MoveUp; virtual;
  282.       function  OverwriteHashStart: BucketRange; virtual;
  283.       function  Parser: PParserObject; virtual;
  284.       procedure PasteBlock(DestBlock: TBlock; Formulas: Word); virtual;
  285.       procedure PasteCellBlock; virtual;
  286.       procedure Print; virtual;
  287.       procedure Recalc(Display: Boolean); virtual;
  288.       function  RowToY(Row: Integer): Byte; virtual;
  289.       function  SameCellPos(P1, P2 : CellPos) : Boolean; virtual;
  290.       procedure ScrollDraw; virtual;
  291.       function  SelectColumn(var Event: TEvent): Boolean; virtual;
  292.       procedure SetAreas(ScrollArea: TRect); virtual;
  293.       procedure SetAvailableCommands; virtual;
  294.       { Enables all commands handled by TSpreadSheet.  Some commands
  295.         may not be enabled if the spreadsheet is protected. }
  296.       {#X SheetProtected }
  297.       procedure SetBlankArea; virtual;
  298.       procedure SetChanged(IsChanged: Boolean); virtual;
  299.       procedure SetFormat(Block: TBlock; DecimalPlaces: Byte; Justification,
  300.         NumberFormat: Word; CurrencyChar: Char); virtual;
  301.       { Formats a block of cells using the given format information.
  302.  
  303.         Possible values of the Justification parameter and their meanings:}
  304.       {#F+}
  305.       {}
  306.       { Value  │  Meaning                }
  307.       { ═══════╪════════════════════════ }
  308.       {   0    │  Left justification     }
  309.       {   1    │  Center justification   }
  310.       {   2    │  Right justification    }
  311.       {#F-}
  312.       { Possible values of the NumberFormat parameter and their meainigs:}
  313.       {#F+}
  314.       {}
  315.       { Value  │ Meaning                                         }
  316.       { ═══════╪════════════════════════════════════════════════ }
  317.       {  0     │ Do not format numbers                           }
  318.       {  1     │ Add a currency character to numbers             }
  319.       {  2     │ Add commas to numbers                           }
  320.       {  3     │ Add commans and a currency character to numbers }
  321.       {#F-}
  322.       procedure SetLimit(X, Y: Integer); virtual;
  323.       procedure SetLocked; virtual;
  324.       procedure SetNameWithMouse(var Event: TEvent); virtual;
  325.       procedure SetProtection(Enable, Display: Boolean); virtual;
  326.       procedure SetScreenColStart(NewCol: Integer); virtual;
  327.       procedure SetScreenColStop(NewCol: Integer); virtual;
  328.       procedure SetScreenRowStart(NewRow: Integer); virtual;
  329.       procedure SetScreenRowStop(NewRow: Integer); virtual;
  330.       procedure SetState(AState: Word; Enable: Boolean); virtual;
  331.       procedure SetUnlocked; virtual;
  332.       procedure SortData; virtual;
  333.       function  SortObject : PSortObject; virtual;
  334.       procedure Store(var S: TStream);
  335.       procedure StoreHashTables(var S: TStream); virtual;
  336.       procedure StoreTablesToTempFile; virtual;
  337.       procedure ToggleAutoCalc; virtual;
  338.       procedure ToggleBlockOn; virtual;
  339.       procedure ToggleDisplayHeaders; virtual;
  340.       procedure ToggleEnd; virtual;
  341.       procedure ToggleFormulaDisplay; virtual;
  342.       function  TrackCursor: Boolean; virtual;
  343.       procedure UpdateScreenBlockDisplay; virtual;
  344.       function  WidthHashStart:BucketRange; virtual;
  345.       function  XToCol(X: Byte): Integer; virtual;
  346.       function  YToRow(Y: Byte): Integer; virtual;
  347.       procedure DoneHashTables; virtual;
  348.       destructor Done; virtual;
  349.    end; {...TSpreadSheet }
  350.  
  351. type
  352.   BlockOperation = (opCopy, opMove);
  353.   { Used by the clipboard record to indicate what kind of operation
  354.     was requested }
  355.  
  356.   ClipBoardRecord = RECORD
  357.   { This record is used to store information necessary for copy and move
  358.     operations }
  359.     Active            : Boolean;
  360.     SourceSpreadSheet : PSpreadSheet;
  361.     SourceCellHash    : PCellHashTable;
  362.     BlockToCopy       : PBlock;
  363.     CopyBlock         : Boolean;
  364.     Operation         : BlockOperation;
  365.   end; {...ClipBoardRecord }
  366.  
  367. var
  368.   Clipboard : ClipBoardRecord;
  369.  
  370. procedure RegisterSpreadSheet;
  371. { Register all the units in OOGrid Library(TM) v1.0 }
  372.  
  373. procedure RegisterGLTSheet;
  374. { Register this unit's objects }
  375.  
  376. const
  377.    RSpreadSheet : TStreamRec = (
  378.       ObjType : stRSpreadSheet;
  379.       VmtLink : Ofs(TypeOf(TSpreadSheet)^);
  380.       Load    : @TSpreadSheet.Load;
  381.       Store   : @TSpreadSheet.Store
  382.    );
  383.  
  384. {****************************************************************************}
  385.                                implementation
  386. {****************************************************************************}
  387.  
  388. uses App, Memory, TCUtil, MsgBox, StdDlg, GLWindow;
  389.  
  390. const
  391.   OOGridFileHeader = 'OOGridLv1.00';
  392.   { All TSpreadSheet objects stored in a stream will be identified by
  393.     this file header.
  394.  
  395.     Version 1.00 refers to the stream version, not to the library's
  396.     version (i.e. it refers to the version of the load and store
  397.     methods). }
  398.  
  399. {****************************************************************************}
  400. {**             Clipboard variables, procedures and functions              **}
  401. {****************************************************************************}
  402.  
  403. procedure InitClipBoard;
  404. { Resets the ClipBoard fields }
  405. begin
  406.   with ClipBoard do
  407.   begin
  408.     Active := False;
  409.     SourceSpreadSheet := nil;
  410.     SourceCellHash := nil;
  411.     if BlockToCopy <> nil then
  412.     begin
  413.       Dispose(BlockToCopy);
  414.       BlockToCopy := nil;
  415.     end; {...if BlockToCopy <> nil }
  416.     Operation := opCopy;
  417.     CopyBlock := False;
  418.   end; {...with ClipBoard }
  419. end; {...InitClipBoard }
  420.  
  421. procedure ToggleClipBoardOn(SpreadSheet: PSpreadSheet; Block: PBlock;
  422.   ABlockOn: Boolean; Op: BlockOperation);
  423. { Sets the Clipboard fields for a copy or move operation }
  424. begin
  425.   with Clipboard do
  426.   begin
  427.     Active := True;
  428.     SourceSpreadSheet := SpreadSheet;
  429.     SourceCellHash := @SpreadSheet^.CellHash;
  430.     BlockToCopy := Block;
  431.     CopyBlock := ABlockOn;
  432.     Operation := Op;
  433.   end; {...with ClipBoard }
  434.   if Op = opCopy then
  435.     begin
  436.       if not DisplayMessage(GLStringList^.Get(sCopyCellsMsg)) then
  437.       begin
  438.         Application^.OutOfMemory;
  439.         InitClipBoard;
  440.       end; {...if not DisplayMessage(GLStringList^.Get(sCopyCellsMsg)) }
  441.     end {...if Op = opCopy }
  442.   else
  443.     begin
  444.       if not DisplayMessage(GLStringList^.Get(sMoveCellsMsg)) then
  445.       begin
  446.         Application^.OutOfMemory;
  447.         InitClipBoard;
  448.       end; {...if not DisplayMessage(GLStringList^.Get(sMoveCellsMsg)) }
  449.     end; {...if/else }
  450. end; {...ToggleClipBoardOn }
  451.  
  452.  
  453. procedure ToggleClipBoardOff;
  454. { Clears the ClipBoard }
  455. begin
  456.   InitClipBoard;
  457.   EraseMessage;
  458. end; {...ToggleClipBoardOff }
  459.  
  460.  
  461. {****************************************************************************}
  462. {**                         GetColWidth function                           **}
  463. {****************************************************************************}
  464.  
  465. function GetColWidth(var WHash : TWidthHashTable; C : Word) : Byte;
  466. { Gets the width of a column }
  467. var
  468.   W : Word;
  469. begin
  470.   W := WHash.Search(C);
  471.   if W = 0 then
  472.     GetColWidth := WHash.GetDefaultColWidth
  473.   else
  474.     GetColWidth := W;
  475. end; {...GetColWidth }
  476.  
  477. {****************************************************************************}
  478. {**                        Unit's Register procedures                      **}
  479. {****************************************************************************}
  480.  
  481. procedure RegisterSpreadSheet;
  482. { Register all streamable objects of the spreadsheet }
  483. begin
  484.    RegisterGLTSheet;
  485.    RegisterGLSupprt;
  486.    RegisterGLCell;
  487.    RegisterGLViews;
  488. end; {...RegisterSpreadSheet }
  489.  
  490. procedure RegisterGLTSheet;
  491. begin
  492.   RegisterType(RSpreadSheet);
  493. end; {...RegisterGLTSheet }
  494.  
  495. {****************************************************************************}
  496. {**                          TSpreadSheet Object                           **}
  497. {****************************************************************************}
  498.  
  499. constructor TSpreadSheet.Init(var Bounds: TRect;
  500.   InitCells: LongInt; AEmptyRowsAtTop, AEmptyRowsAtBottom: Byte;
  501.   AHScrollBar, AVScrollBar: PScrollBar; AInitMaxCols,
  502.   AInitMaxRows: Integer; InitDefaultColWidth, InitDefaultDecimalPlaces,
  503.   InitMaxDecimalPlaces: Byte; InitDefaultCurrency: CurrencyStr);
  504. const
  505.   MinRowsToDisplay = 2;
  506. var
  507.   CellPosition : CellPos;
  508.   R : TRect;
  509. begin
  510.   if not TScroller.Init(Bounds, AHScrollBar, AVScrollBar) then
  511.     Fail;
  512.   Delta.X := 1;
  513.   Delta.Y := 1;
  514.   EventMask := evMouseDown + evKeyDown + evCommand + evBroadCast;
  515.   Options := Options and not ofBuffered;
  516.   GrowMode := gfGrowHiX + gfGrowHiY;
  517.   if HScrollBar <> nil then
  518.   begin
  519.     HScrollBar^.EventMask := HScrollBar^.EventMask and not evKeyDown;
  520.     with PLimScrollBar(HScrollBar)^ do
  521.     begin
  522.       DisplayLimit := TCUtil.Min(DisplayLimit, AInitMaxCols);
  523.     end; { with }
  524.   end; { if }
  525.   if VScrollBar <> nil then
  526.   begin
  527.     VScrollBar^.EventMask := VScrollBar^.EventMask and not evKeyDown;
  528.     with PLimScrollBar(VScrollBar)^ do
  529.     begin
  530.       DisplayLimit := TCUtil.Min(DisplayLimit, AInitMaxRows);
  531.     end; { with }
  532.   end; { if }
  533.   if not CellHash.Init(CellHashStart(InitCells)) then
  534.     Fail;
  535.   if not WidthHash.Init(WidthHashStart, InitDefaultColWidth) then
  536.   begin
  537.     CellHash.Done;
  538.     Fail;
  539.   end; {...if not WidthHash.Init }
  540.   if not OverwriteHash.Init(OverwriteHashStart) then
  541.   begin
  542.     CellHash.Done;
  543.     WidthHash.Done;
  544.     Fail;
  545.   end; {...if not OverWriteHash.Init }
  546.   if not FormatHash.Init then
  547.   begin
  548.     CellHash.Done;
  549.     WidthHash.Done;
  550.     OverwriteHash.Done;
  551.     Fail;
  552.   end; {...if not FormatHash.Init }
  553.   if not ColHeadersHash.Init(ColHeadersHashStart) then
  554.   begin
  555.     CellHash.Done;
  556.     WidthHash.Done;
  557.     OverWriteHash.Done;
  558.     FormatHash.Done;
  559.     Fail;
  560.   end; {...if not ColHeadersHash.Init }
  561.   if not UnlockedHash.Init then
  562.   begin
  563.     CellHash.Done;
  564.     WidthHash.Done;
  565.     OverWriteHash.Done;
  566.     FormatHash.Done;
  567.     ColHeadersHash.Done;
  568.     Fail;
  569.   end; {...if not UnlockedHash.Init }
  570.   EmptyRowsAtTop := AEmptyRowsAtTop;
  571.   EmptyRowsAtBottom := AEmptyRowsAtBottom;
  572.   RowNumberSpace := 6;
  573.   MaxColWidth := Succ(ScreenCols - RowNumberSpace);
  574.   MaxScreenCols := MaxColWidth div DefaultMinColWidth;
  575.   GetMem(ColStart, MaxScreenCols);
  576.   if ColStart = nil then
  577.   begin
  578.     CellHash.Done;
  579.     WidthHash.Done;
  580.     OverWriteHash.Done;
  581.     FormatHash.Done;
  582.     ColHeadersHash.Done;
  583.     UnlockedHash.Done;
  584.     Fail;
  585.   end; {...if ColStart = nil }
  586.   InitCurrPos;
  587.   OldCurrPos := CurrPos;
  588.   LastPos := CurrPos;
  589.   BlockOn := False;
  590.   AutoCalc := False;
  591.   DisplayFormulas := False;
  592.   GoToEnd := False;
  593.   ScreenBlock := New(PBlock, Init(CurrPos));
  594.   CurrBlock := New(PBlock, Init(CurrPos));
  595.   DefaultColWidth := InitDefaultColWidth;
  596.   DefaultDecimalPlaces := InitDefaultDecimalPlaces;
  597.   DefaultCurrency := InitDefaultCurrency;
  598.   MaxDecimalPlaces := InitMaxDecimalPlaces;
  599.   MaxCols := AInitMaxCols;
  600.   MaxRows := AInitMaxRows;
  601.   GetExtent(R);
  602.   Inc(R.A.Y, EmptyRowsAtTop);
  603.   Dec(R.B.Y, EmptyRowsAtBottom);
  604.   SetAreas(R);
  605.   SetLimit(MaxCols, MaxRows);
  606.   DisplayHeaders := True;
  607.   SetProtection(False, False);
  608.   SetAvailableCommands;
  609.   MessageDialog := nil;
  610. end; {...TSpreadSheet.Init }
  611.  
  612.  
  613. function TSpreadSheet.AddCell(CellType: CellTypes; Pos: CellPos;
  614.   Error: Boolean; Value: Extended; Input: String): Boolean;
  615. { Adds a cell to the cell hash }
  616. var
  617.   OldLastPos : CellPos;
  618.   CellPtr, CP : PCell;
  619. begin
  620.   AddCell := False;
  621.   case CellType of
  622.     ClValue   : CellPtr := New(PValueCell, Init(Pos, Error, Value));
  623.     ClFormula : CellPtr := New(PFormulaCell, Init(Pos, Error, Value, Input));
  624.     ClText    : CellPtr := New(PTextCell, Init(Pos, Input));
  625.     ClRepeat  : CellPtr := New(PRepeatCell, Init(Pos, Input[2]));
  626.   end; {...case CellType }
  627.   if CellPtr = nil then
  628.     Exit;
  629.   if not CellHash.Add(CellPtr) then
  630.   begin
  631.     Dispose(CellPtr, Done);
  632.     Exit;
  633.   end; {...if not CellHash.Add(CellPtr) }
  634.   OldLastPos := LastPos;
  635.   FindLastPos(Pos);
  636.   if not OverWriteHash.Add(CellPtr, CellHash, FormatHash, WidthHash, LastPos,
  637.     MaxCols, GetColWidth, DisplayFormulas, ChangeYes) then
  638.   begin
  639.     LastPos := OldLastPos;
  640.     CellHash.Delete(CellPtr^.Loc, CP);
  641.     Dispose(CellPtr, Done);
  642.     Exit;
  643.   end; {...if not OverWriteHash.Add }
  644.   AddCell := True;
  645. end; {...TSpreadSheet.AddCell }
  646.  
  647.  
  648. function TSpreadSheet.CellHashStart(TotalCells: LongInt): BucketRange;
  649. { Returns the initial number of buckets for the Cell hash table }
  650. begin
  651.   CellHashStart := Max(100, Min(MaxBuckets, TotalCells div 10));
  652. end; {...TSpreadSheet.CellHashStart}
  653.  
  654.  
  655. function TSpreadSheet.CellsProtected(Block: TBlock): Boolean;
  656. var
  657.   P : CellPos;
  658. begin
  659.   CellsProtected := False;
  660.   if SheetProtected then
  661.   begin
  662.     for P.Row := Block.Start.Row to Block.Stop.Row do
  663.       for P.Col := Block.Start.Col to Block.Stop.Col do
  664.         if not UnlockedHash.Search(P) then
  665.         begin
  666.           CellsProtected := True;
  667.           Exit;
  668.         end; {...if not UnlockedHash.Search(P) }
  669.   end; {...if SheetProtected }
  670. end; {...TSpreadSheet.CellsProtected }
  671.  
  672.  
  673. function TSpreadSheet.CellToFString(P: CellPos; var AColor: Byte): String;
  674. { Returns the formatted contents of a cell to be displayed in the screen }
  675. var
  676.   ColorFound, HasError : Boolean;
  677.   S1 : CurrencyStr;
  678.   F  : FormatType;
  679.   CP : PCell;
  680.   S  : String;
  681.   ClType : CellTypes;
  682. begin
  683.   AColor := FStringSituationColor(P, CP, HasError, ColorFound);
  684.   if HasError and not (DisplayFormulas and (CP^.CellType = ClFormula)) then
  685.     begin
  686.       S := GLStringList^.Get(sCellError);
  687.       S1 := '';
  688.       F := Ord(JCenter) shl JustShift;
  689.     end {...if HasError and ... }
  690.   else
  691.     begin
  692.       S := CP^.FormattedString(OverwriteHash, FormatHash, WidthHash,
  693.         GetColWidth, P, DisplayFormulas, 1, ColWidth(P.Col), S1, ClType);
  694.       if not ColorFound then
  695.         case ClType of
  696.           ClEmpty : AColor := GetColor(1);
  697.           ClText : AColor := GetColor(3);
  698.           ClValue : AColor := GetColor(2);
  699.           ClFormula : if DisplayFormulas then
  700.                         AColor := GetColor(5)
  701.                       else
  702.                         AColor := GetColor(2);
  703.           ClRepeat : AColor := GetColor(4);
  704.         end; {...case ClType }
  705.       F := CP^.Format(FormatHash, DisplayFormulas);
  706.     end; {...if/else }
  707.   if (Length(S1) + Length(S)) <= ColWidth(P.Col) then
  708.     case Justification((F shr JustShift) and JustPart) of
  709.       JLeft : CellToFString := S1 + LeftJustStr(S, ColWidth(P.Col) -
  710.                 Length(S1));
  711.       JCenter : CellToFString := S1 + CenterStr(S, ColWidth(P.Col) -
  712.                   Length(S1));
  713.       JRight : CellToFString := S1 + RightJustStr(S, ColWidth(P.Col) -
  714.                  Length(S1));
  715.     end {...case Justification((F shr JustShift) and JustPart) }
  716.   else
  717.     CellToFString := Copy(S1 + S, 1, ColWidth(P.Col));
  718. end; {...TSpreadSheet.CellToFString }
  719.  
  720.  
  721. procedure TSpreadSheet.ChangeBounds(var Bounds: TRect);
  722. { Changes the size of the spreadsheet and resets the limits of the scroller }
  723. begin
  724.   TScroller.ChangeBounds(Bounds);
  725.   SetLimit(MaxCols, MaxRows);
  726. end; {...TSpreadSheet.ChangeBounds }
  727.  
  728.  
  729. {****************************************************************************}
  730. { TSpreadSheet.ChangeColHeaders                                              }
  731. {****************************************************************************}
  732. procedure TSpreadSheet.ChangeColHeaders;
  733. { Changes the header of a column or group of columns }
  734. var
  735.   Cancel, HeaderEntered : Boolean;
  736.   Dialog : PDialog;
  737.   CellPtr : PCell;
  738.   Column : Word;
  739.  
  740.     procedure GetValidHeader;
  741.     { Returns WidthEntered as true if a valid width was entered }
  742.     var
  743.       Code : Integer;
  744.     begin
  745.       if Desktop^.ExecView(Dialog) <> cmCancel then
  746.         begin
  747.           Dialog^.GetData(RChangeHeader);
  748.           HeaderEntered := True;
  749.         end {...if Desktop^.ExecView(Dialog) <> cmCancel }
  750.       else
  751.         Cancel := True;
  752.     end; {...GetValidHeader }
  753.  
  754. begin
  755.   Cancel := False;
  756.   HeaderEntered := False;
  757.   Dialog := PDialog(GLResFile^.Get('ChangeHeaderDialog'));
  758.   if not BlockOn or (BlockOn and (CurrBlock^.Start.Col = CurrBlock^.Stop.Col)) then
  759.     begin
  760.       if not ColHeadersHash.Search(CurrPos.Col, RChangeHeader.NewHeader) then
  761.         RChangeHeader.NewHeader := GLStringList^.Get(sColumnEntryIndicator) +
  762.         ' '+ColumnToString(CurrPos.Col)
  763.     end {...if not BlockOn or ... }
  764.   else
  765.     RChangeHeader.NewHeader := '';
  766.   Dialog^.SetData(RChangeHeader);
  767.   repeat
  768.     if (Application^.ValidView(Dialog) <> nil) then
  769.       GetValidHeader
  770.     else
  771.       Exit;
  772.   until HeaderEntered or Cancel;
  773.   if not Cancel then
  774.   begin
  775.     with RChangeHeader do
  776.     begin
  777.       if Copy(NewHeader, 1, Length(GLStringList^.Get(sColumnEntryIndicator)))
  778.          = GLStringList^.Get(sColumnEntryIndicator) then
  779.         NewHeader := Copy(NewHeader, Length(GLStringList^.
  780.           Get(sColumnEntryIndicator))+2, Length(NewHeader) -
  781.           Length(GLStringList^.Get(sColumnEntryIndicator))+1);
  782.       if not BlockOn then
  783.       begin
  784.         CurrBlock^.Start := CurrPos;
  785.         CurrBlock^.Stop := CurrPos;
  786.       end; { if }
  787.       ChangeHeader(CurrBlock, 0, NewHeader);
  788.       SetChanged(ModifiedYes);
  789.     end; { with }
  790.     DrawView;
  791.   end; {...if not Cancel }
  792.   Dispose(Dialog, Done);
  793. end; {...TSpreadSheet.ChangeColHeaders }
  794.  
  795.  
  796. {****************************************************************************}
  797. { TSpreadSheet.ChangeColWidth                                                }
  798. {****************************************************************************}
  799. procedure TSpreadSheet.ChangeColWidth;
  800. { Changes the width of a column or group of columns }
  801. var
  802.   Cancel, WidthEntered : Boolean;
  803.   NewWidth : Byte;
  804.   Dialog : PDialog;
  805.   CellPtr : PCell;
  806.   CurrWidth : String[10];
  807.   CellsOverWritten : Word;
  808.  
  809.     procedure GetValidWidth(Dialog: PDialog; var Cancel,
  810.       WidthEntered: Boolean; var NewWidth: Byte);
  811.       { Returns WidthEntered as true if a valid width was entered }
  812.     var
  813.       Code : Integer;
  814.     begin
  815.       if Desktop^.ExecView(Dialog) <> cmCancel then
  816.         begin
  817.           Dialog^.GetData(RChangeWidth);
  818.           Val(RChangeWidth.NewWidth, NewWidth, Code);
  819.           if not ((NewWidth >= DefaultMinColWidth) and
  820.              (NewWidth <= MaxColWidth) or (NewWidth = 0)) then
  821.             MessageBox(GLStringList^.Get(sInvalidWidthMsg), nil, mfError +
  822.               mfOKButton)
  823.           else
  824.             begin
  825.               WidthEntered := True;
  826.               if NewWidth = 0 then NewWidth := DefaultColWidth;
  827.             end; {...if/else }
  828.         end {...if Desktop^.ExecView(Dialog) <> cmCancel }
  829.       else
  830.         Cancel := True;
  831.    end; {...GetValidWidth }
  832.  
  833. begin
  834.   Cancel := False;
  835.   WidthEntered := False;
  836.   Dialog := PDialog(GLResFile^.Get('GetWidthDialog'));
  837.   if (not BlockOn) or (BlockOn and
  838.      (CurrBlock^.Start.Col = CurrBlock^.Stop.Col)) then
  839.     Str(ColWidth(CurrPos.Col), CurrWidth)
  840.   else
  841.     Str(DefaultColWidth, CurrWidth);
  842.   Dialog^.SetData(CurrWidth);
  843.   repeat
  844.     if (Application^.ValidView(Dialog) <> nil) then
  845.       GetValidWidth(Dialog, Cancel, WidthEntered, NewWidth)
  846.     else
  847.       Exit;
  848.   until WidthEntered or Cancel;
  849.   if not Cancel then
  850.   begin
  851.     if not BlockOn then
  852.     begin
  853.       CurrBlock^.Start := CurrPos;
  854.       CurrBlock^.Stop := CurrPos;
  855.     end; { if }
  856.     ChangeWidth(CurrBlock, 0, NewWidth);
  857.     SetChanged(ModifiedYes);
  858.     SetScreenColStart(ScreenBlock^.Start.Col);
  859.     if CurrPos.Col > ScreenBlock^.Stop.Col then
  860.       HScrollBar^.SetValue(CurrPos.Col);
  861.     DrawView;
  862.   end; {...if not Cancel }
  863.   Dispose(Dialog, Done);
  864. end; {...TSpreadSheet.ChangeColWidth }
  865.  
  866.  
  867. {****************************************************************************}
  868. { TSpreadSheet.ChangeHeader                                                  }
  869. {****************************************************************************}
  870. procedure TSpreadSheet.ChangeHeader(Block: PBlock; AColumn: Word; NewHeader:
  871.   String);
  872. var
  873.   Column : Word;
  874.   Pos: CellPos;
  875.   SingleCol: Boolean;
  876. begin
  877.   if Block = nil then
  878.     begin
  879.       Pos.Col := AColumn;
  880.       Pos.Row := 0;
  881.       Block := New(PBlock, Init(Pos));
  882.       SingleCol := True;
  883.     end { if }
  884.   else
  885.     SingleCol := False;
  886.   with ColHeadersHash, Block^ do
  887.   begin
  888.     for Column := Start.Col to Stop.Col do
  889.     begin
  890.       if NewHeader <> ColToString(Column) then
  891.       begin
  892.         Delete(Column);
  893.         if (NewHeader <> '') then
  894.         begin
  895.           if not Add(Column, NewHeader) then
  896.             Exit;
  897.         end; {...if NewHeader <> '' }
  898.       end; {...if NewHeader <> ColToString(Column) }
  899.       Delete(Column);
  900.       if (NewHeader <> '') and (NewHeader <> ColToString(Column)) then
  901.       begin
  902.         if not Add(Column, NewHeader) then
  903.           Exit;
  904.       end; {...if (NewHeader <> '') and ... }
  905.     end; {...for Column }
  906.   end; {...with ColHeadersHash, CurrBlock^ }
  907.   if SingleCol then
  908.     Dispose(Block, Done);
  909. end;
  910.  
  911. {****************************************************************************}
  912. { TSpreadSheet.ChangeWidth                                                   }
  913. {****************************************************************************}
  914. procedure TSpreadSheet.ChangeWidth(Block: PBlock; AColumn: Word; NewWidth:
  915.   Byte);
  916. var
  917.   Column : Word;
  918.   Pos: CellPos;
  919.   SingleCol: Boolean;
  920. begin
  921.   if Block = nil then
  922.     begin
  923.       Pos.Col := AColumn;
  924.       Pos.Row := 0;
  925.       Block := New(PBlock, Init(Pos));
  926.       SingleCol := True;
  927.     end { if }
  928.   else
  929.     SingleCol := False;
  930.   with WidthHash, Block^ do
  931.   begin
  932.     for Column := Start.Col to Stop.Col do
  933.     begin
  934.       Delete(Column);
  935.       if NewWidth <> DefaultColWidth then
  936.       begin
  937.         if not Add(Column, NewWidth) then
  938.           Exit;
  939.       end; {...if NewWidth <> DefaultColWidth }
  940.     end; {...for Column }
  941.   end; { with }
  942.   with OverWriteHash do
  943.   begin
  944.     Done;
  945.     Init(OverWriteHashStart);
  946.   end; {with OverWriteHash }
  947.   FixOverWrite;
  948.   if SingleCol then
  949.     Dispose(Block, Done);
  950. end;
  951.  
  952. procedure TSpreadSheet.CheckForDragging;
  953. var
  954.   ShiftState : Byte absolute $40:$17;
  955. begin
  956.   if (ShiftState and (kbRightShift + kbLeftShift)) <> 0 then
  957.     begin
  958.       if not BlockOn then
  959.         ToggleBlockOn;
  960.     end {...if ShiftState and (kbRightShift + kbLeftShift) }
  961.   else
  962.     ClearCurrBlock;
  963. end; {...TSpreadSheet.CheckForDragging }
  964.  
  965.  
  966. procedure TSpreadSheet.ClearCurrBlock;
  967. { Turns off the block mode and redisplays the affected cells }
  968. begin
  969.   if BlockOn then
  970.   begin
  971.     BlockOn := False;
  972.     DisplayBlock(CurrBlock^);
  973.   end; {...if BlockOn }
  974.   DisplayInfo;
  975. end; {...TSpreadSheet.ClearCurrBlock }
  976.  
  977.  
  978. procedure TSpreadSheet.ClearScreenArea(AreaToClear: PScreenArea);
  979. { Clears a given area of the screen }
  980. var
  981.   W, H : Byte;
  982.   B : TDrawBuffer;
  983. begin
  984.   with AreaToClear^ do
  985.   begin
  986.     W := Succ(LowerRight.Col - UpperLeft.Col);
  987.     H := Succ(LowerRight.Row - UpperLeft.Row);
  988.     MoveChar(B, ' ', Attrib, W);
  989.     WriteLine(UpperLeft.Col, UpperLeft.Row, W, H, B);
  990.   end; {...with AreaToClear^ }
  991. end; {...TSpreadSheet.ClearScreenArea }
  992.  
  993.  
  994. function TSpreadSheet.ColHeadersHashStart: BucketRange;
  995. { Returns the initial number of buckets for the Column Names hash table }
  996. begin
  997.   ColHeadersHashStart := 10;
  998. end; {...TSpreadSheet.ColHeadersHashStart }
  999.  
  1000.  
  1001. function TSpreadSheet.ColumnToString(Column: Word): String;
  1002. { Converts a column to a string }
  1003. var
  1004.   HasName : Boolean;
  1005.   S : String[4];
  1006.   Name : String;
  1007.   W : Word;
  1008. begin
  1009.   HasName := ColHeadersHash.Search(Column, Name);
  1010.   if DisplayHeaders and HasName then
  1011.     ColumnToString := Name
  1012.   else
  1013.     begin
  1014.       if Column > 18278 then                     { Column is 4 letters }
  1015.         S := Chr(Ord('A') + ((Column - 18279) div 17576))
  1016.       else
  1017.         S := '';
  1018.       if Column > 702 then                { Column is at least 3 letters }
  1019.         S := S + Chr(Ord('A') + (((Column - 703) mod 17576) div 676));
  1020.       if Column > 26 then                 { Column is at least 2 letters }
  1021.         S := S + Chr(Ord('A') + (((Column - 27) mod 676) div 26));
  1022.       S := S + Chr(Ord('A') + (Pred(Column) mod 26));
  1023.       ColumnToString := S;
  1024.    end; {...if/else }
  1025. end; {...TSpreadSheet.ColumnToString }
  1026.  
  1027.  
  1028. function TSpreadsheet.ColToX(Col : Integer): Byte;
  1029. { Returns the screen position of a given column }
  1030. begin
  1031.   ColToX := ColStart^[Col - ScreenBlock^.Start.Col];
  1032. end; {...TSpreadSheet.ColToX }
  1033.  
  1034.  
  1035. function TSpreadSheet.ColWidth(Col: Integer): Byte;
  1036. { Returns the width of a certain column }
  1037. var
  1038.   Width : Integer;
  1039. begin
  1040.   Width := WidthHash.Search(Col);
  1041.   if Width = 0 then
  1042.     ColWidth := DefaultColWidth
  1043.   else
  1044.     ColWidth := Width;
  1045. end; {...TSpreadSheet.ColWidth }
  1046.  
  1047.  
  1048. procedure TSpreadSheet.CopyCellBlock;
  1049. { Activates the clipboard and sets it to indicate the block to be copied }
  1050. var
  1051.   Block : PBlock;
  1052. begin
  1053.   if BlockOn then
  1054.     begin
  1055.       New(Block, Init(CurrBlock^.Start));
  1056.       if Block = nil then
  1057.         Exit;
  1058.       Block^.Stop := CurrBlock^.Stop;
  1059.     end {...if BlockOn }
  1060.   else
  1061.     begin
  1062.       New(Block, Init(CurrPos));
  1063.       if Block = nil then
  1064.         Exit;
  1065.       Block^.Stop := CurrPos;
  1066.     end; {...if/else }
  1067.   ToggleClipBoardOn(@Self, Block, BlockOn, opCopy);
  1068. end; {...TSpreadSheet.CopyCellBlock }
  1069.  
  1070.  
  1071. procedure TSpreadSheet.DeleteBlock(Block: TBlock; var Deleted: Boolean);
  1072. { Deletes a block of cells }
  1073. var
  1074.   H, D : HashItemPtr;
  1075.   CellPtr : PCell;
  1076.   Counter : Word;
  1077. begin
  1078.   Deleted := False;
  1079.   with CellHash, Block do
  1080.   begin
  1081.     for Counter := 1 to Buckets do
  1082.     begin
  1083.       H := HashData^[Counter];
  1084.       while H <> nil do
  1085.       begin
  1086.         D := H;
  1087.         H := H^.Next;
  1088.         Move(D^.Data, CellPtr, Sizeof(CellPtr));
  1089.         with CellPtr^ do
  1090.         begin
  1091.           if CellInBlock(Loc) then
  1092.             DeleteCell(Loc, Deleted);
  1093.         end; {...with CellPtr^ }
  1094.       end; {...while H <> nil }
  1095.     end; {...for Counter }
  1096.   end; {...with CellHash, Block }
  1097. end; {...TSpreadSheet.DeleteBlock }
  1098.  
  1099.  
  1100. procedure TSpreadSheet.DeleteCell(Pos: CellPos; var Deleted: Boolean);
  1101. { Deletes a single cell }
  1102. var
  1103.   CellPtr : PCell;
  1104. begin
  1105.   CellHash.Delete(Pos, CellPtr);
  1106.   if CellPtr <> nil then
  1107.     begin
  1108.       OverWriteHash.Delete(Pos, CellHash, FormatHash, WidthHash, LastPos,
  1109.         MaxCols, GetColWidth, DisplayFormulas, ChangeYes);
  1110.       Dispose(CellPtr, Done);
  1111.       Deleted := True;
  1112.     end {...if CellPtr <> nil }
  1113.   else
  1114.     Deleted := False;
  1115. end; {...TSpreadSheet.DeleteCell}
  1116.  
  1117.  
  1118. procedure TSpreadSheet.DeleteColFromHash(Block: TBlock; Columns, EndDelCol:
  1119.   Word);
  1120. { Deletes a column or block of columns from the hash tables }
  1121. var
  1122.   Pos, Start, Stop : CellPos;
  1123.   H : HashItemPtr;
  1124.   CellPtr : PCell;
  1125.   Col : Word;
  1126.   F : File;
  1127.   Deleted : Boolean;
  1128. const
  1129.   CopyFormulasLiteral = $03;
  1130. begin
  1131.   SetChanged(ModifiedYes);
  1132.   DeleteBlock(Block, Deleted);
  1133.   with CellHash do
  1134.   begin
  1135.     CellPtr := FirstItem;
  1136.     while CellPtr <> nil do
  1137.     begin
  1138.       with CellPtr^ do
  1139.       begin
  1140.         if CellPtr^.ShouldUpdate then
  1141.            FixFormulaCol(CellPtr, opDelete, EndDelCol, Columns, MaxCols,
  1142.              MaxRows);
  1143.       end; {...with CellPtr^ }
  1144.       CellPtr := NextItem;
  1145.     end; {...while CellPtr <> nil }
  1146.   end; {...with CellHash }
  1147.  
  1148.   for Col := Block.Start.Col to Block.Stop.Col do
  1149.     WidthHash.Delete(Col);
  1150.   with WidthHash do
  1151.   begin
  1152.     H := FirstItem;
  1153.     while H <> nil do
  1154.     begin
  1155.       if WordPTr(@H^.Data)^ > EndDelCol then
  1156.         Dec(WordPtr(@H^.Data)^, Columns);
  1157.       H := NextItem;
  1158.     end; {...with H <> nil }
  1159.   end; {...with WidthHash }
  1160.  
  1161.   Stop.Col := Block.Stop.Col;
  1162.   Stop.Row := MaxInt;
  1163.   FormatHash.Delete(Block.Start, Stop);
  1164.   with FormatHash do
  1165.   begin
  1166.     H := FirstItem;
  1167.     while H <> nil do
  1168.     begin
  1169.       Move(H^.Data, Start, SizeOf(Start));
  1170.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1171.       if (Start.Col > (EndDelCol - Columns)) and (Stop.Col <= EndDelCol) then
  1172.         Delete(Start, Stop)
  1173.       else
  1174.         begin
  1175.           if Start.Col > EndDelCol then
  1176.           begin
  1177.             Dec(Start.Col, Columns);
  1178.             Move(Start, H^.Data, Sizeof(Start));
  1179.           end; {...if Start.Col > EndDelCol }
  1180.           if Stop.Col > EndDelCol then
  1181.           begin
  1182.             Dec(Stop.Col, Columns);
  1183.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1184.           end; {...if Stop.Col > EndDelCol }
  1185.         end; {...if/else }
  1186.       H := NextItem;
  1187.     end; {...while H <> nil }
  1188.   end; {...with FormatHash }
  1189.  
  1190.   DeleteColHeaders(@Block);
  1191.   with ColHeadersHash do
  1192.   begin
  1193.     H := FirstItem;
  1194.     while H <> nil do
  1195.     begin
  1196.       if WordPTr(@H^.Data)^ > EndDelCol then
  1197.         Dec(WordPtr(@H^.Data)^, Columns);
  1198.       H := NextItem;
  1199.     end; {...with H <> nil }
  1200.   end; {...with ColHeadersHash }
  1201.  
  1202.   Stop.Col := Block.Stop.Col;
  1203.   Stop.Row := MaxInt;
  1204.   UnlockedHash.Delete(Block.Start, Stop);
  1205.   with UnlockedHash do
  1206.   begin
  1207.     H := FirstItem;
  1208.     while (H <> nil) do
  1209.     begin
  1210.       Move(H^.Data, Start, SizeOf(Start));
  1211.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1212.       if (Start.Col > (EndDelCol - Columns)) and (Stop.Col <= EndDelCol) then
  1213.         Delete(Start, Stop)
  1214.       else
  1215.         begin
  1216.           if Start.Col > EndDelCol then
  1217.           begin
  1218.             Dec(Start.Col, Columns);
  1219.             Move(Start, H^.Data, Sizeof(Start));
  1220.           end; {...if Start.Col > EndDelCol }
  1221.           if Stop.Col > EndDelCol then
  1222.           begin
  1223.             Dec(Stop.Col, Columns);
  1224.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1225.           end; {...if Stop.Col > EndDelCol }
  1226.         end; {...if/else }
  1227.       H := NextItem;
  1228.     end; {...while H <> nil }
  1229.   end; {...with UnlockedHash }
  1230.  
  1231.   StoreTablesToTempFile;
  1232.   DoneHashTables;
  1233.   Pos.Col := Succ(EndDelCol);
  1234.   Pos.Row := 0;
  1235.   LoadTablesFromTempFile(Pos, 0, -Columns);
  1236.   Assign(F, GLStringList^.Get(sTempFileName));
  1237.   Erase(F);
  1238.   if LastPos.Col > 1 then
  1239.     Dec(LastPos.Col, Columns);
  1240.   Pos.Col := EndDelCol - Columns;
  1241.   if Deleted then
  1242.     Pos.Row := LastPos.Row
  1243.   else
  1244.     Pos.Row := 1;
  1245.   FindLastPos(Pos);
  1246.   FixOverWrite;
  1247. end; {...TSpreadSheet.DeleteColFromHash }
  1248.  
  1249.  
  1250. procedure TSpreadSheet.DeleteColHeaders(Block: PBlock);
  1251. { Deletes from the column headers hash table the headers of the selected
  1252.   columns }
  1253. var
  1254.   C : Word;
  1255. begin
  1256.   SetChanged(ModifiedYes);
  1257.   with Block^ do
  1258.   begin
  1259.     if Start.Col = Stop.Col then
  1260.       ColHeadersHash.Delete(Start.Col)
  1261.     else
  1262.       for C := Start.Col to Stop.Col do
  1263.         ColHeadersHash.Delete(C);
  1264.   end; {...with Block^ }
  1265. end; {...TSpreadSheet.DeleteColHeaders }
  1266.  
  1267.  
  1268. procedure TSpreadSheet.DeleteColumns;
  1269. { Deletes a column or group of columns }
  1270. var
  1271.   Start, Stop : CellPos;
  1272.   H : HashItemPtr;
  1273.   CellPtr : PCell;
  1274.   Block : TBlock;
  1275.   Columns, EndDelCol : Word;
  1276.   S : TBufStream;
  1277.   Items: LongInt;
  1278. begin
  1279.   Block.Start.Col := 0;
  1280.   Block.Start.Row := 0;
  1281.   Block.Stop.Col := 0;
  1282.   Block.Stop.Row := 0;
  1283.   if BlockOn then
  1284.     begin
  1285.       if CurrBlock^.Start.Col <= LastPos.Col then
  1286.       begin
  1287.         with Block do
  1288.         begin
  1289.           Start.Col := CurrBlock^.Start.Col;
  1290.           Start.Row := 1;
  1291.           if CurrBlock^.Stop.Col > LastPos.Col then
  1292.             Stop.Col := LastPos.Col
  1293.           else
  1294.             Stop.Col := CurrBlock^.Stop.Col;
  1295.           Stop.Row := LastPos.Row;
  1296.         end; {...with Block }
  1297.       end; {...if CurrBlock^.Start.Col <= LastPos.Col }
  1298.       Columns := Succ(CurrBlock^.Stop.Col - CurrBlock^.Start.Col);
  1299.       EndDelCol := CurrBlock^.Stop.Col;
  1300.     end {...if BlockOn }
  1301.   else
  1302.     begin
  1303.       if CurrPos.Col <= LastPos.Col then
  1304.       begin
  1305.         with Block do
  1306.         begin
  1307.           Start.Col := CurrPos.Col;
  1308.           Start.Row := 1;
  1309.           Stop.Col := CurrPos.Col;
  1310.           Stop.Row := LastPos.Row;
  1311.         end; {...with Block }
  1312.       end; {...if CurrPos.Col <= LastPos.Col }
  1313.       Columns := 1;
  1314.       EndDelCol := CurrPos.Col;
  1315.     end; {...if/else }
  1316.   MessageDialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  1317.   if Application^.ValidView(MessageDialog) <> nil then
  1318.     Desktop^.Insert(MessageDialog)
  1319.   else
  1320.     begin
  1321.       MessageDialog := nil;
  1322.       Exit;
  1323.     end; { else }
  1324.   DeleteColFromHash(Block, Columns, EndDelCol);
  1325.   SetScreenColStart(ScreenBlock^.Start.Col);
  1326.   if AutoCalc then
  1327.     Recalc(DisplayNo);
  1328.   if MessageDialog <> nil then
  1329.   begin
  1330.     Desktop^.Delete(MessageDialog);
  1331.     Dispose(MessageDialog, Done);
  1332.     MessageDialog := nil;
  1333.   end; { if }
  1334.   DrawView;
  1335. end; {...TSpreadSheet.DeleteColumns }
  1336.  
  1337.  
  1338. procedure TSpreadSheet.DeleteRowFromHash(Block: TBlock; Rows, EndDelRow:
  1339.   Word);
  1340. { Deletes a row or block of rows from the hash tables }
  1341. var
  1342.   Pos, Start, Stop : CellPos;
  1343.   H : HashItemPtr;
  1344.   CellPtr : PCell;
  1345.   Deleted : Boolean;
  1346.   F : File;
  1347. begin
  1348.   SetChanged(ModifiedYes);
  1349.   DeleteBlock(Block, Deleted);
  1350.   with CellHash do
  1351.   begin
  1352.     CellPtr := FirstItem;
  1353.     while CellPtr <> nil do
  1354.     begin
  1355.       with CellPtr^ do
  1356.       begin
  1357.         if CellPtr^.ShouldUpdate then
  1358.           FixFormulaRow(CellPtr, opDelete, EndDelRow, Rows, MaxCols, MaxRows);
  1359.       end; {...with CellPtr^ }
  1360.       CellPtr := NextItem;
  1361.     end; {...while CellPtr <> nil }
  1362.   end; {...with CellHash }
  1363.  
  1364.   Stop.Col := MaxInt;
  1365.   Stop.Row := Block.Stop.Row;
  1366.   FormatHash.Delete(Block.Start, Stop);
  1367.   with FormatHash do
  1368.   begin
  1369.     H := FirstItem;
  1370.     while H <> nil do
  1371.     begin
  1372.       Move(H^.Data, Start, SizeOf(Start));
  1373.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1374.       if (Start.Row > (EndDelRow - Rows)) and (Stop.Row <= EndDelRow) then
  1375.         Delete(Start, Stop)
  1376.       else
  1377.         begin
  1378.           if Start.Row > EndDelRow then
  1379.           begin
  1380.             Dec(Start.Row, Rows);
  1381.             Move(Start, H^.Data, Sizeof(Start));
  1382.           end; {...if Start.Row > EndDelRow }
  1383.           if Stop.Row > EndDelRow then
  1384.           begin
  1385.             Dec(Stop.Row, Rows);
  1386.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1387.           end; {...if Stop.Row > EndDelRow }
  1388.         end; {...if/else }
  1389.       H := NextItem;
  1390.     end; {...while H <> nil }
  1391.   end; {...with FormatHash }
  1392.  
  1393.   Stop.Col := MaxInt;
  1394.   Stop.Row := Block.Stop.Row;
  1395.   UnlockedHash.Delete(Block.Start, Stop);
  1396.   with UnlockedHash do
  1397.   begin
  1398.     H := FirstItem;
  1399.     while H <> nil do
  1400.     begin
  1401.       Move(H^.Data, Start, SizeOf(Start));
  1402.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1403.       if (Start.Row > (EndDelRow - Rows)) and (Stop.Row <= EndDelRow) then
  1404.         Delete(Start, Stop)
  1405.       else
  1406.         begin
  1407.           if Start.Row > EndDelRow then
  1408.           begin
  1409.             Dec(Start.Row, Rows);
  1410.             Move(Start, H^.Data, Sizeof(Start));
  1411.           end; {...if Start.Row > EndDelRow }
  1412.           if Stop.Row > EndDelRow then
  1413.           begin
  1414.             Dec(Stop.Row, Rows);
  1415.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1416.           end; {...if Stop.Row > EndDelRow }
  1417.         end; {...if/else }
  1418.       H := NextItem;
  1419.     end; {...while H <> nil }
  1420.   end; {...with UnlockedHash }
  1421.  
  1422.   StoreTablesToTempFile;
  1423.   DoneHashTables;
  1424.   Pos.Col := 0;
  1425.   Pos.Row := Succ(EndDelRow);
  1426.   LoadTablesFromTempFile(Pos, -Rows, 0);
  1427.   Assign(F, GLStringList^.Get(sTempFileName));
  1428.   Erase(F);
  1429.   if LastPos.Row > 1 then
  1430.     Dec(LastPos.Row, Rows);
  1431.   Pos.Row := EndDelRow - Rows;
  1432.   if Deleted then
  1433.     Pos.Col := LastPos.Col
  1434.   else
  1435.     Pos.Col := 1;
  1436.   FindLastPos(Pos);
  1437.   SetChanged(ModifiedYes);
  1438.   FixOverWrite;
  1439. end; {...TSpreadSheet.DeleteRowFromHash }
  1440.  
  1441.  
  1442. procedure TSpreadSheet.DeleteRows;
  1443. { Deletes a row or a group of rows }
  1444. var
  1445.   Start, Stop : CellPos;
  1446.   H : HashItemPtr;
  1447.   CellPtr : PCell;
  1448.   Block : TBlock;
  1449.   EndDelRow, Rows : Word;
  1450. begin
  1451.   Block.Start.Col := 0;
  1452.   Block.Start.Row := 0;
  1453.   Block.Stop.Col := 0;
  1454.   Block.Stop.Row := 0;
  1455.   if BlockOn then
  1456.     begin
  1457.       if CurrBlock^.Start.Row <= LastPos.Row then
  1458.       begin
  1459.         with Block do
  1460.         begin
  1461.           Start.Col := 1;
  1462.           Start.Row := CurrBlock^.Start.Row;
  1463.           Stop.Col := LastPos.Col;
  1464.           if CurrBlock^.Stop.Row > LastPos.Row then
  1465.             Stop.Row := LastPos.Row
  1466.           else
  1467.             Stop.Row := CurrBlock^.Stop.Row;
  1468.         end; {...with Block }
  1469.       end; {...if CurrBlock^.Start.Row <= LastPos.Row }
  1470.       Rows := Succ(CurrBlock^.Stop.Row - CurrBlock^.Start.Row);
  1471.       EndDelRow := CurrBlock^.Stop.Row;
  1472.     end {...if BlockOn }
  1473.   else
  1474.     begin
  1475.       if CurrPos.Row <= LastPos.Row then
  1476.       begin
  1477.         with Block do
  1478.         begin
  1479.           Start.Col := 1;
  1480.           Start.Row := CurrPos.Row;
  1481.           Stop.Col := LastPos.Col;
  1482.           Stop.Row := CurrPos.Row;
  1483.         end; {...with Block }
  1484.       end; {if CurrPos.Row <= LastPos.Row }
  1485.       Rows := 1;
  1486.       EndDelRow := CurrPos.Row;
  1487.     end; {...if/else }
  1488.   MessageDialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  1489.   if Application^.ValidView(MessageDialog) <> nil then
  1490.     Desktop^.Insert(MessageDialog)
  1491.   else
  1492.     begin
  1493.       MessageDialog := nil;
  1494.       Exit;
  1495.     end; { else }
  1496.   DeleteRowFromHash(Block, Rows, EndDelRow);
  1497.   if AutoCalc then
  1498.     Recalc(DisplayNo);
  1499.   if MessageDialog <> nil then
  1500.   begin
  1501.     Desktop^.Delete(MessageDialog);
  1502.     Dispose(MessageDialog, Done);
  1503.     MessageDialog := nil;
  1504.   end; { if }
  1505.   DrawView;
  1506. end; {...TSpreadSheet.DeleteRows }
  1507.  
  1508.  
  1509. procedure TSpreadSheet.DisplayAllCells;
  1510. { Displays all the cells in the current screen block }
  1511. begin
  1512.   ClearScreenArea(@DisplayArea);
  1513.   DisplayBlock(ScreenBlock^);
  1514. end; {...TSpreadSheet.DisplayAllCells }
  1515.  
  1516.  
  1517. procedure TSpreadSheet.DisplayBlock(B: TBlock);
  1518. { Displays a block of cells }
  1519. begin
  1520.   with B do
  1521.     DisplayCellBlock(Start.Col, Start.Row, Succ(Stop.Col), Stop.Row);
  1522. end; {...TSpreadSheet.DisplayBlock }
  1523.  
  1524.  
  1525. procedure TSpreadsheet.DisplayBlockDiff(B1, B2 : TBlock);
  1526. { Displays the cells present in one block, not present in the another block }
  1527. var
  1528.   Pass : Byte;
  1529.   B : TBlock;
  1530.   RefBlock, Block2, TempBlock : PBlock;
  1531. begin
  1532.   if Compare(B1, B2, SizeOf(TBlock)) then
  1533.     Exit;
  1534.   Pass := 0;
  1535.   RefBlock := @B1;
  1536.   Block2 := @B2;
  1537.   repeat
  1538.     Inc(Pass);
  1539.     if Block2^.Start.Col < RefBlock^.Start.Col then
  1540.     begin
  1541.       if Block2^.Start.Row < RefBlock^.Start.Row then
  1542.       begin
  1543.         B.Start := Block2^.Start;
  1544.         B.Stop.Col := Pred(RefBlock^.Start.Col);
  1545.         B.Stop.Row := Pred(RefBlock^.Start.Row);
  1546.         DisplayBlock(B);
  1547.       end; {...if Block2^.Start.Row < RefBlock^.Start.Row }
  1548.       if (Block2^.Start.Row >= RefBlock^.Start.Row) and
  1549.          (Block2^.Start.Row <= RefBlock^.Stop.Row) then
  1550.         begin
  1551.           B.Start.Col := Block2^.Start.Col;
  1552.           B.Start.Row := Block2^.Start.Row;
  1553.           B.Stop.Col := Pred(RefBlock^.Start.Col);
  1554.           B.Stop.Row := RefBlock^.Stop.Row;
  1555.           DisplayBlock(B);
  1556.         end {...if (Block2^.Start.Row >= RefBlock^.Start.Row) and ... }
  1557.       else if Block2^.Stop.Row <= RefBlock^.Stop.Row then
  1558.         begin
  1559.           B.Start.Col := Block2^.Start.Col;
  1560.           B.Start.Row := RefBlock^.Start.Row;
  1561.           B.Stop.Col := Pred(RefBlock^.Start.Col);
  1562.           B.Stop.Row := Min(RefBlock^.Stop.Row, Block2^.Stop.Row);
  1563.           DisplayBlock(B);
  1564.         end; {...else if Block2^.Stop.Row <= RefBlock^.Stop.Row }
  1565.       if Block2^.Stop.Row > RefBlock^.Stop.Row then
  1566.       begin
  1567.         B.Start.Col := Block2^.Start.Col;
  1568.         B.Start.Row := Succ(RefBlock^.Stop.Row);
  1569.         B.Stop.Col := Pred(RefBlock^.Start.Col);
  1570.         B.Stop.Row := Block2^.Stop.Row;
  1571.         DisplayBlock(B);
  1572.       end; {...if Block2^.Stop.Row > RefBlock^.Stop.Row }
  1573.     end; {...if Block2^.Start.Col < RefBlock^.Start.Col }
  1574.  
  1575.     if Block2^.Start.Row < RefBlock^.Start.Row then
  1576.     begin
  1577.       if (Block2^.Start.Col >= RefBlock^.Start.Col) and
  1578.          (Block2^.Start.Col <= RefBlock^.Stop.Col) then
  1579.         begin
  1580.           B.Start.Col := Block2^.Start.Col;
  1581.           B.Start.Row := Block2^.Start.Row;
  1582.           B.Stop.Col := RefBlock^.Stop.Col;
  1583.           B.Stop.Row := Pred(RefBlock^.Start.Row);
  1584.           DisplayBlock(B);
  1585.         end {...if (Block2^.Start.Col >= RefBlock^.Start.Col) and ... }
  1586.       else if Block2^.Stop.Col <= RefBlock^.Stop.Col then
  1587.         begin
  1588.           B.Start.Col := RefBlock^.Start.Col;
  1589.           B.Start.Row := Block2^.Start.Row;
  1590.           B.Stop.Col := Min(RefBlock^.Stop.Col, Block2^.Stop.Col);
  1591.           B.Stop.Row := Pred(RefBlock^.Start.Row);
  1592.           DisplayBlock(B);
  1593.         end; {...else if Block2^.Stop.Col <= RefBlock^.Stop.Col }
  1594.     end; {...if Block2^.Start.Row < RefBlock^.Start.Row }
  1595.  
  1596.     if Block2^.Stop.Row > RefBlock^.Stop.Row then
  1597.     begin
  1598.       if (Block2^.Start.Col >= RefBlock^.Start.Col) and
  1599.          (Block2^.Start.Col <= RefBlock^.Stop.Col) then
  1600.         begin
  1601.           B.Start.Col := Block2^.Start.Col;
  1602.           B.Start.Row := Succ(RefBlock^.Stop.Row);
  1603.           B.Stop.Col := RefBlock^.Stop.Col;
  1604.           B.Stop.Row := Block2^.Stop.Row;
  1605.           DisplayBlock(B);
  1606.         end {...if (Block2^.Start.Col >= RefBlock^.Start.Col) and ... }
  1607.       else if Block2^.Stop.Col <= RefBlock^.Stop.Col then
  1608.         begin
  1609.           B.Start.Col := RefBlock^.Start.Col;
  1610.           B.Start.Row := Succ(RefBlock^.Stop.Row);
  1611.           B.Stop.Col := Min(RefBlock^.Stop.Row, Block2^.Stop.Row);
  1612.           B.Stop.Row := Block2^.Stop.Row;
  1613.           DisplayBlock(B);
  1614.         end; {...else if Block2^.Stop.Col <= RefBlock^.Stop.Col }
  1615.     end; {...if Block2^.Stop.Row > RefBlock^.Stop.Row }
  1616.  
  1617.     if Block2^.Stop.Col > RefBlock^.Stop.Col then
  1618.     begin
  1619.       if Block2^.Start.Row < RefBlock^.Start.Row then
  1620.       begin
  1621.         B.Start.Col := Succ(RefBlock^.Stop.Col);
  1622.         B.Start.Row := Block2^.Start.Row;
  1623.         B.Stop.Col := Block2^.Stop.Col;
  1624.         B.Stop.Row := Pred(RefBlock^.Start.Row);
  1625.         DisplayBlock(B);
  1626.       end; {...if Block2^.Start.Row < RefBlock^.Start.Row }
  1627.       if (Block2^.Start.Row >= RefBlock^.Start.Row) and
  1628.          (Block2^.Start.Row <= RefBlock^.Stop.Row) then
  1629.         begin
  1630.           B.Start.Col := Succ(RefBlock^.Stop.Col);
  1631.           B.Start.Row := Block2^.Start.Row;
  1632.           B.Stop.Col := Block2^.Stop.Col;
  1633.           B.Stop.Row := RefBlock^.Stop.Row;
  1634.           DisplayBlock(B);
  1635.         end {...if (Block2^.Start.Row >= RefBlock^.Start.Row) and ... }
  1636.       else if Block2^.Stop.Row <= RefBlock^.Stop.Row then
  1637.         begin
  1638.           B.Start.Col := Succ(RefBlock^.Stop.Col);
  1639.           B.Start.Row := RefBlock^.Start.Row;
  1640.           B.Stop.Col := Block2^.Stop.Col;
  1641.           B.Stop.Row := Min(RefBlock^.Stop.Row, Block2^.Stop.Row);
  1642.           DisplayBlock(B);
  1643.         end; {...else if Block2^.Stop.Row <= RefBlock^.Stop.Row }
  1644.       if Block2^.Stop.Row > RefBlock^.Stop.Row then
  1645.       begin
  1646.         B.Start.Col := Succ(RefBlock^.Stop.Col);
  1647.         B.Start.Row := Succ(RefBlock^.Stop.Row);
  1648.         B.Stop := Block2^.Stop;
  1649.         DisplayBlock(B);
  1650.       end; {...if Block2^.Stop.Row > RefBlock^.Stop.Row }
  1651.     end; {...if Block2^.Stop.Col > RefBlock^.Stop.Col }
  1652.  
  1653.     TempBlock := RefBlock;
  1654.     RefBlock := Block2;
  1655.     Block2 := TempBlock;
  1656.   until (Pass = 2);
  1657. end; {...TSpreadSheet.DisplayBlockDiff }
  1658.  
  1659.  
  1660. procedure TSpreadsheet.DisplayCell(P : CellPos);
  1661. { Displays a single cell }
  1662. var
  1663.   Color : Byte;
  1664.   S     : String[ScreenCols];
  1665.   B     : TDrawBuffer;
  1666.   Col   : Byte;
  1667. begin
  1668.   S := CellToFString(P, Color);
  1669.   MoveStr(B, S, Color);
  1670.   Col := ColToX(P.Col);
  1671.   WriteLine(Col, RowToY(P.Row), Min(Length(S), (Size.X - Col)), 1, B);
  1672. end; {...TSpreadSheet.DisplayCell }
  1673.  
  1674.  
  1675. procedure TSpreadSheet.DisplayCellBlock(C1, R1, C2, R2: Word);
  1676. { Displays a block of cells }
  1677. var
  1678.   P : CellPos;
  1679. begin
  1680.   with ScreenBlock^ do
  1681.   begin
  1682.     for P.Row := Max(R1, Start.Row) to Min(R2, Stop.Row) do
  1683.       for P.Col := Max(C1, Start.Col) to Min(C2, Succ(Stop.Col)) do
  1684.          DisplayCell(P);
  1685.   end; {...with ScreenBlock^ }
  1686. end; {...TSpreadSheet.DisplayCellBlock }
  1687.  
  1688.  
  1689. procedure TSpreadSheet.DisplayCellData;
  1690. var
  1691.   InfoStringLength, W  : Byte;
  1692.   CP : PCell;
  1693.   CurrWidth, LockedState, S  : String;
  1694.   B  : TDrawBuffer;
  1695.   Pos : CellPos;
  1696. const
  1697.   BlockInfoSize = 30;
  1698.   CellInfoSize = 28;
  1699. begin
  1700.   if (State and sfActive <> 0) then
  1701.     Pos := CurrPos
  1702.   else
  1703.     Pos := OldCurrPos;
  1704.   CP := CellHash.Search(Pos);
  1705.   ClearScreenArea(@DataArea);
  1706.   Str(ColWidth(Pos.Col), CurrWidth);
  1707.   LockedState := '';
  1708.   if UnlockedHash.Search(Pos) then
  1709.     LockedState := GLStringList^.Get(sCellUnLockedInfo)
  1710.   else
  1711.     if SheetProtected then
  1712.       LockedState := GLStringList^.Get(sCellLockedInfo);
  1713.   with DataArea do
  1714.   begin
  1715.     S := LeftJustStr(ColToString(Pos.Col) + RowToString(Pos.Row) +
  1716.       ' [' + GLStringList^.Get(sWidthLetter) + CurrWidth + '] ' + CP^.Name +
  1717.       ' ' + LockedState, CellInfoSize);
  1718.     InfoStringLength := CellInfoSize;
  1719.     if BlockOn then
  1720.     begin
  1721.       with CurrBlock^ do
  1722.       begin
  1723.         S := S + LeftJustStr(GLStringList^.Get(sBlockName) +
  1724.           ColToString(Start.Col) + RowToString(Start.Row) + '..' +
  1725.           ColToString(Stop.Col) + RowToString(Stop.Row), BlockInfoSize);
  1726.         InfoStringLength := InfoStringLength + BlockInfoSize
  1727.       end; {...with CurrBlock^ }
  1728.     end; {...if BlockOn }
  1729.     MoveStr(B, S, GetColor(8));
  1730.     WriteLine(UpperLeft.Col, UpperLeft.Row, InfoStringLength, 1, B);
  1731.   end; {...with DataArea }
  1732.   with ContentsArea do
  1733.   begin
  1734.     S := LeftJustStr(CP^.DisplayString(DisplayFormulas, MaxDecimalPlaces),
  1735.       Succ(LowerRight.Col-UpperLeft.Col));
  1736.     MoveStr(B, S, GetColor(9));
  1737.     WriteLine(UpperLeft.Col, UpperLeft.Row, Length(S), 1, B);
  1738.   end; {...with ContenstArea }
  1739. end; {...TSpreadSheet.DisplayCellData }
  1740.  
  1741.  
  1742. procedure TSpreadSheet.DisplayCols;
  1743. { Displays the column headers }
  1744. var
  1745.   W, X : Byte;
  1746.   C : Integer;
  1747.   B : TDrawBuffer;
  1748. begin
  1749.   with ScreenBlock^ do
  1750.   begin
  1751.     if not NoBlankArea then
  1752.     begin
  1753.       X := ColStart^[Stop.Col - Start.Col]+ColWidth(Stop.Col);
  1754.       W := Max(Size.X - X, Size.X);
  1755.       MoveChar(B, ' ', ColArea.Attrib, W);
  1756.       WriteLine(X, ColArea.UpperLeft.Row, W, 1, B);
  1757.     end; {...if not NoBlankArea }
  1758.     for C := Start.Col to Min(Succ(Stop.Col), MaxCols) do
  1759.     begin
  1760.       W := ColWidth(C);
  1761.       MoveStr(B, CenterStr(ColumnToString(C), W), ColArea.Attrib);
  1762.       WriteLine (ColStart^[C - Start.Col], ColArea.UpperLeft.Row, W, 1, B);
  1763.     end; {...for C }
  1764.   end; {...with ScreenBlock^ }
  1765. end; {...TSpreadSheet.DisplayCols }
  1766.  
  1767.  
  1768. procedure TSpreadSheet.DisplayInfo;
  1769. { Displays the spreadsheet's info characters }
  1770. var
  1771.   Width     : Byte;
  1772.   Info      : String;
  1773.   B         : TDrawBuffer;
  1774. begin
  1775.   ClearScreenArea(@InfoArea);
  1776.   with InfoArea do
  1777.   begin
  1778.     Width := Succ(LowerRight.Col - UpperLeft.Col);
  1779.     Info := ColToString(GetNumber);
  1780.     if Modified then
  1781.       Info := Copy(Info, 1, 1) + '*';{Chr(4);}
  1782.     if Length(Info) = 1 then
  1783.       Info := Info + ' ';
  1784.     if GoToEnd then
  1785.       Info := Info + GLStringList^.Get(sEndKeyPressedLetter)
  1786.     else
  1787.       Info := Info + ' ';
  1788.     if DisplayHeaders then
  1789.       Info := Info + GLStringList^.Get(sDisplayHeadersLetter)
  1790.     else
  1791.       Info := Info + ' ';
  1792.     if AutoCalc then
  1793.       Info := Info + GLStringList^.Get(sAutoCalcLetter)
  1794.     else
  1795.       Info := Info + ' ';
  1796.     if DisplayFormulas then
  1797.       Info := Info + GLStringList^.Get(sDisplayFormulasLetter)
  1798.     else
  1799.       Info := Info + ' ';
  1800.     MoveStr(B, Info, Attrib);
  1801.     Writeline (UpperLeft.Col, UpperLeft.Row, Min(Width, Length(Info)), 1, B);
  1802.   end; {...with InfoArea }
  1803. end; {...TSpreadSheet.DisplayInfo }
  1804.  
  1805.  
  1806. procedure TSpreadSheet.DisplayRows;
  1807. { Displays row numbers }
  1808. var
  1809.   R : Integer;
  1810.   B : TDrawBuffer;
  1811. begin
  1812.   with ScreenBlock^ do
  1813.   begin
  1814.     for R := Start.Row to Stop.Row do
  1815.       with RowArea do
  1816.       begin
  1817.         MoveStr(B, LeftJustStr(RowToString(R), RowNumberSpace),
  1818.           RowArea.Attrib);
  1819.         WriteLine(UpperLeft.Col, R - Start.Row + UpperLeft.Row,
  1820.           RowNumberSpace, 1, B);
  1821.       end; {...with RowArea }
  1822.   end; {...with ScreenBlock^ }
  1823. end; {...TSpreadSheet.DisplayRows }
  1824.  
  1825.  
  1826. {****************************************************************************}
  1827. { TSpreadSheet.DoAfterAddingCell                                             }
  1828. {****************************************************************************}
  1829. procedure TSpreadSheet.DoAfterAddingCell;
  1830. { This procedure is called after a cell is added or modified }
  1831. begin
  1832.   MoveDown;
  1833. end;
  1834.  
  1835. {****************************************************************************}
  1836. { TSpreadsheet.DoBeforeAddingCell                                            }
  1837. {****************************************************************************}
  1838. function TSpreadsheet.DoBeforeAddingCell;
  1839. begin
  1840.   DoBeforeAddingCell := True;
  1841. end;
  1842.  
  1843. {****************************************************************************}
  1844. { TSpreadSheet.DragCursorWithMouse                                           }
  1845. {****************************************************************************}
  1846. procedure TSpreadSheet.DragCursorWithMouse(Event: TEvent);
  1847. { Sets block mode on and extends the block to wherever the mouse is pointing }
  1848. var
  1849.    ColScrPos : Byte;
  1850.    OldPos : CellPos;
  1851.    Counter : Integer;
  1852.    Mouse : TPoint;
  1853. begin
  1854.   MakeLocal(Event.Where, Mouse);
  1855.   with ScreenBlock^ do
  1856.   begin
  1857.     KeyPressed := True;
  1858.     if not BlockOn then ToggleBlockOn;
  1859.     OldPos := CurrPos;
  1860.     if Mouse.Y < DisplayArea.UpperLeft.Row then
  1861.       begin
  1862.         CurrPos.Row := Max(1, Pred(Start.Row));
  1863.         SetScreenRowStart(CurrPos.Row);
  1864.         VScrollBar^.SetValue(ScreenBlock^.Start.Row);
  1865.       end {...if Mouse.Y < DisplayArea.UpperLeft.Row }
  1866.     else if Mouse.Y > DisplayArea.LowerRight.Row then
  1867.       begin
  1868.         CurrPos.Row := Min(MaxRows, Succ(Stop.Row));
  1869.         SetScreenRowStop(CurrPos.Row);
  1870.         VScrollBar^.SetValue(ScreenBlock^.Start.Row);
  1871.       end {...if Mouse.Y > DisplayArea.LowerRight.Row }
  1872.     else
  1873.       CurrPos.Row := YToRow(Mouse.Y);
  1874.     if (Mouse.X >= Size.X) then
  1875.       begin
  1876.         CurrPos.Col := Min(MaxCols, Succ(Stop.Col));
  1877.         SetScreenColStop(CurrPos.Col);
  1878.         HScrollBar^.SetValue(ScreenBlock^.Start.Col);
  1879.       end {...if (Mouse.X >= Size.X) or... }
  1880.     else if Mouse.X < RowNumberSpace then
  1881.       begin
  1882.         CurrPos.Col := Max(1, Pred(Start.Col));
  1883.         SetScreenColStart(CurrPos.Col);
  1884.         HScrollBar^.SetValue(ScreenBlock^.Start.Col);
  1885.       end {...else if Mouse.X < RowNumberSpace }
  1886.     else
  1887.       CurrPos.Col := XToCol(Mouse.X);
  1888.     MoveCell(OldPos);
  1889.     KeyPressed := False;
  1890.   end; {...with ScreenBlock^ }
  1891. end; {...TSpreadSheet.DragCursorWithMouse }
  1892.  
  1893.  
  1894. procedure TSpreadSheet.Draw;
  1895. { Sets the spreadsheet areas and displays all the spreadsheet's components }
  1896. var
  1897.   R : TRect;
  1898. begin
  1899.   GetExtent(R);
  1900.   Inc(R.A.Y, EmptyRowsAtTop);
  1901.   Dec(R.B.Y, EmptyRowsAtBottom);
  1902.   SetAreas(R);
  1903.   DisplayCols;
  1904.   DisplayRows;
  1905.   DisplayInfo;
  1906.   DisplayAllCells;
  1907.   DisplayCellData;
  1908. end; {...TSpreadSheet.Draw }
  1909.  
  1910.  
  1911. procedure TSpreadSheet.EraseCellBlock(EraseBlock: Boolean);
  1912. { Deletes a cell or block of cells }
  1913. var
  1914.   Deleted: Boolean;
  1915.   Pos : CellPos;
  1916. begin
  1917.   Deleted := False;
  1918.   if not BlockOn or not EraseBlock then
  1919.     begin
  1920.       if not SheetProtected or (SheetProtected and
  1921.          UnlockedHash.Search(CurrPos)) then
  1922.         begin
  1923.           DeleteCell(CurrPos, Deleted);
  1924.           Pos := CurrPos;
  1925.         end {...if not SheetProtected or ... }
  1926.       else
  1927.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), nil, mfInformation +
  1928.           mfOKButton);
  1929.     end {...if not BlockOn or not EraseBlock }
  1930.   else
  1931.     begin
  1932.       if not CellsProtected(CurrBlock^) then
  1933.         begin
  1934.           DisplayMessage(GLStringList^.Get(sBlockDeleteMsg));
  1935.           DeleteBlock(CurrBlock^, Deleted);
  1936.           EraseMessage;
  1937.           Pos := CurrBlock^.Stop;
  1938.           if Deleted then
  1939.             ClearCurrBlock;
  1940.         end {...if not CellsProtected(CurrBlock^) }
  1941.       else
  1942.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), nil, mfInformation +
  1943.           mfOKButton);
  1944.     end; {...if/else }
  1945.   if Deleted then
  1946.   begin
  1947.     Desktop^.Lock;
  1948.     FindLastPos(Pos);
  1949.     SetChanged(ModifiedYes);
  1950.     if AutoCalc then
  1951.       Recalc(DisplayYes);
  1952.     DisplayAllCells;
  1953.     DisplayCellData;
  1954.     Desktop^.Unlock;
  1955.   end; {...if Deleted }
  1956. end; {...TSpreadSheet.EraseCellBlock }
  1957.  
  1958.  
  1959. procedure TSpreadSheet.ExtendCurrBlock(Redraw : Boolean);
  1960. { Resizes the current block if active }
  1961. var
  1962.   OldBlock : TBlock;
  1963. begin
  1964.   if BlockOn then
  1965.   begin
  1966.     Move(CurrBlock^, OldBlock, SizeOf(CurrBlock^));
  1967.     if CurrBlock^.ExtendTo(CurrPos) then
  1968.       begin
  1969.         if Redraw then
  1970.           DisplayBlockDiff(OldBlock, CurrBlock^);
  1971.       end {...if CurrBlock^.ExtendTo(CurrPos) }
  1972.     else
  1973.       ClearCurrBlock;
  1974.   end; {...if BlockOn }
  1975. end; {...TSpreadSheet.ExtendCurrBlock }
  1976.  
  1977.  
  1978. procedure TSpreadsheet.FindLastPos(DPos : CellPos);
  1979. { Finds the lower left corner of smallest block containing used cells }
  1980. var
  1981.   ColFound, RowFound : Boolean;
  1982.   CellPtr : PCell;
  1983.   Counter : Word;
  1984. begin
  1985.   with CellHash do
  1986.   begin
  1987.     ColFound := DPos.Col < LastPos.Col;
  1988.     RowFound := DPos.Row < LastPos.Row;
  1989.     if (not ColFound) or (not RowFound) then
  1990.     begin
  1991.       if not ColFound then
  1992.         LastPos.Col := 1;
  1993.       if not RowFound then
  1994.         LastPos.Row := 1;
  1995.       CellPtr := FirstItem;
  1996.       while CellPtr <> nil do
  1997.       begin
  1998.         if not ColFound then
  1999.         begin
  2000.           if CellPtr^.Loc.Col > LastPos.Col then
  2001.           begin
  2002.             LastPos.Col := CellPtr^.Loc.Col;
  2003.             if HScrollBar <> nil then
  2004.               PLimScrollBar(HScrollBar)^.DisplayLimit :=
  2005.                 Max(DefaultHScrollBarLimit, LastPos.Col);
  2006.             ColFound := LastPos.Col = DPos.Col;
  2007.             if ColFound and RowFound then
  2008.               Exit;
  2009.           end; {...if CellPtr^.Loc.Col > LastPos.Col }
  2010.         end; {...if not ColFound }
  2011.         if not RowFound then
  2012.         begin
  2013.           if CellPtr^.Loc.Row > LastPos.Row then
  2014.           begin
  2015.             LastPos.Row := CellPtr^.Loc.Row;
  2016.             if VScrollBar <> nil then
  2017.               PLimScrollBar(VScrollBar)^.DisplayLimit :=
  2018.                 Max(DefaultVScrollBarLimit, LastPos.Row);
  2019.             RowFound := LastPos.Row = DPos.Row;
  2020.             if ColFound and RowFound then
  2021.               Exit;
  2022.           end; {...if CellPtr^.Loc.Row > LastPos.Row }
  2023.         end; {...if not RowFound }
  2024.         CellPtr := NextItem;
  2025.       end; {...while CellPtr <> nil }
  2026.     end; {...if (not ColFound) or (not RowFound) }
  2027.   end; {...with CellHash }
  2028. end; {...TSpreadSheet.FindLastPos }
  2029.  
  2030.  
  2031.  
  2032. procedure TSpreadSheet.FindScreenColStart;
  2033. { Find the starting screen column when the ending column is known}
  2034. var
  2035.   Temp, Width : Byte;
  2036.   Index, Place : Integer;
  2037. begin
  2038.   with ScreenBlock^ do
  2039.   begin
  2040.     Index := 0;
  2041.     Place := Succ(DisplayArea.LowerRight.Col);
  2042.     Width := ColWidth(Stop.Col);
  2043.     repeat
  2044.       ColStart^[Index] := Max(DisplayArea.UpperLeft.Col, Place - Width);
  2045.       Dec(Place, Width);
  2046.       Inc(Index);
  2047.       if (Stop.Col - Index = 0) then
  2048.         Width := 0
  2049.       else
  2050.         Width := ColWidth(Stop.Col - Index);
  2051.     until (Width = 0) or (Place - Width < DisplayArea.UpperLeft.Col);
  2052.     Start.Col := Succ(Stop.Col - Index);
  2053.     Dec(Index);
  2054.     if ColStart^[Index] > DisplayArea.UpperLeft.Col then
  2055.     begin
  2056.       Temp := ColStart^[Index] - DisplayArea.UpperLeft.Col;
  2057.       for Place := 0 to Index do
  2058.         Dec(ColStart^[Place], Temp);
  2059.     end; {...if ColStart^[Index] > DisplayArea.UpperLeft.Col }
  2060.     if Index > 0 then
  2061.     begin
  2062.       for Place := 0 to (Pred(Index) shr 1) do
  2063.         begin
  2064.           Temp := ColStart^[Index - Place];
  2065.           ColStart^[Index - Place] := ColStart^[Place];
  2066.           ColStart^[Place] := Temp;
  2067.         end; {...for Place }
  2068.     end; {...if Index > 0 }
  2069.     ColStart^[Succ(Index)] := ColStart^[Index] + ColWidth(Stop.Col);
  2070.   end; {...with ScreenBlock^ }
  2071. end; {...TSpreadSheet.FindScreenColStart }
  2072.  
  2073.  
  2074.  
  2075. procedure TSpreadSheet.FindScreenColStop;
  2076. { Finds then ending screen column when the starting column is known }
  2077. var
  2078.   Index, Place, Width : Byte;
  2079. begin
  2080.   with ScreenBlock^ do
  2081.   begin
  2082.     for Index := 1 to 10 do
  2083.       ColStart^[Index] := 0;
  2084.     Index := 0;
  2085.     Place := DisplayArea.UpperLeft.Col;
  2086.     Width := ColWidth(Start.Col);
  2087.     repeat
  2088.       ColStart^[Index] := Place;
  2089.       Inc(Place, Width);
  2090.       Inc(Index);
  2091.       if (Integer(Index) + Start.Col > MaxCols) then
  2092.         Width := 0
  2093.       else
  2094.         Width := ColWidth(Index + Start.Col);
  2095.     until (Width = 0) or
  2096.     (Place + Width > Succ(DisplayArea.LowerRight.Col));
  2097.     ColStart^[Index] := Place;
  2098.     Stop.Col := Pred(Start.Col + Index);
  2099.   end; {...with ScreenBlock^ }
  2100. end; {...TSpreadSheet.FindScreenColStop }
  2101.  
  2102.  
  2103. procedure TSpreadSheet.FindScreenRowStart;
  2104. { Finds the starting screen row when the ending row is know }
  2105. begin
  2106.   with ScreenBlock^ do
  2107.   begin
  2108.     if LongInt(Stop.Row) - TotalRows < 0 then
  2109.       begin
  2110.         Start.Row := 1;
  2111.         FindScreenRowStop;
  2112.       end {if LongInt(Stop.Row) - TotalRows < 0 }
  2113.     else
  2114.       Start.Row := Succ(Stop.Row - TotalRows);
  2115.   end; {...with ScreenBlock^ }
  2116. end; {...TSpreadSheet.FindScreenRowStart }
  2117.  
  2118.  
  2119. procedure TSpreadSheet.FindScreenRowStop;
  2120. { Finds the ending screen row when the starting row is know }
  2121. begin
  2122.   with ScreenBlock^ do
  2123.   begin
  2124.     if LongInt(Start.Row) + TotalRows > Succ(LongInt(MaxRows)) then
  2125.       begin
  2126.         Stop.Row := MaxRows;
  2127.         FindScreenRowStart;
  2128.       end {if (LongInt(Start.Row) + TotalRows) > Succ(MaxRows) }
  2129.     else
  2130.       Stop.Row := Pred(Start.Row + TotalRows);
  2131.   end; {...with ScreenBlock^ }
  2132. end; {...TSpreadSheet.FindScreenRowStop }
  2133.  
  2134.  
  2135. procedure TSpreadSheet.FixBlockOverWrite(Block: TBlock);
  2136. { Updates the overwrite information of a block of cells
  2137.   IMPORTANT: No memory checking is done since it is assumed that no
  2138.              cells were added to the block being updated }
  2139. var
  2140.   CP, D : PCell;
  2141. begin
  2142.   with CellHash do
  2143.   begin
  2144.     CP := FirstItem;
  2145.     while CP <> nil do
  2146.     begin
  2147.       if Block.CellInBlock(CP^.Loc) then
  2148.       begin
  2149.         OverWriteHash.Delete(CP^.Loc, CellHash, FormatHash, WidthHash,
  2150.           LastPos, MaxCols, GetColWidth, DisplayFormulas, ChangeNo);
  2151.         OverwriteHash.Add(CP, CellHash, FormatHash, WidthHash, LastPos,
  2152.           MaxCols, GetColWidth, DisplayFormulas, ChangeNo);
  2153.       end; {...if Block.CellInBlock(CP^.Loc) }
  2154.       CP := NextItem;
  2155.     end; {...while CP <> nil}
  2156.   end; {...with CellHash }
  2157. end; {...TSpreadSheet.FixBlockOverWrite }
  2158.  
  2159.  
  2160. function TSpreadsheet.FixOverWrite: Boolean;
  2161. { Updates the overwrite information for each cell in the spreadsheet }
  2162. var
  2163.   CP, D : PCell;
  2164. begin
  2165.   FixOverWrite := False;
  2166.   with CellHash do
  2167.   begin
  2168.     CP := FirstItem;
  2169.     while CP <> nil do
  2170.     begin
  2171.       if not OverwriteHash.Add(CP, CellHash, FormatHash, WidthHash, LastPos,
  2172.          MaxCols, GetColWidth, DisplayFormulas, ChangeYes) then
  2173.       begin
  2174.         CellHash.Delete(CP^.Loc, D);
  2175.         Dispose(D, Done);
  2176.         Exit;
  2177.       end; {...if not OverwriteHash.Add }
  2178.       CP := NextItem;
  2179.     end; {...while CP <> nil }
  2180.   end; {...with CellHash }
  2181.   FixOverWrite := True;
  2182. end; {...TSpreadSheet.FixOverWrite }
  2183.  
  2184.  
  2185. procedure TSpreadSheet.FormatDefault;
  2186. { Clears the custom assigned formats of a block of cells }
  2187. var
  2188.   Block : TBlock;
  2189. begin
  2190.   with Block do
  2191.   begin
  2192.     if BlockOn then
  2193.       begin
  2194.         Start := CurrBlock^.Start;
  2195.         Stop := CurrBlock^.Stop;
  2196.       end {...if BlockOn }
  2197.     else
  2198.       begin
  2199.         Start := CurrPos;
  2200.         Stop := CurrPos;
  2201.       end; {...if/else }
  2202.   end; {...with Block }
  2203.   if not FormatHash.Delete(Block.Start, Block.Stop) then
  2204.     Exit;
  2205.   SetChanged(ModifiedYes);
  2206.   FixBlockOverWrite(Block);
  2207.   Block.Stop.Col := ScreenBlock^.Stop.Col;
  2208.   DisplayBlock(Block);
  2209. end; {...TSpreadSheet.FormatDefault }
  2210.  
  2211.  
  2212. function TSpreadSheet.FStringSituationColor(P: CellPos; var CP: PCell;
  2213.                       var HasError, ColorFound: Boolean): Byte;
  2214. { Returns situation especific colors of the string to be displayed in the
  2215.   screen (for example: highlighted cell color, cell in block color, etc). }
  2216.  
  2217.   function DisplayErrorColor: Boolean;
  2218.   { This function determines if the cell must be displayed in error color.
  2219.     When the cell is a formula cell and DisplayFormulas mode is on, even
  2220.     though HasError may return true, the cell should not be displayed
  2221.     in error color }
  2222.   begin
  2223.     DisplayErrorColor := HasError and not (DisplayFormulas
  2224.                          and (CP^.CellType = ClFormula));
  2225.   end; {...DisplayErrorColor }
  2226.  
  2227. begin
  2228.   ColorFound := True;
  2229.   CP := CellHash.Search(P);
  2230.   HasError := CP^.HasError;
  2231.   if not SheetProtected or (SheetProtected and not UnlockedHash.Search(P)) then
  2232.     begin
  2233.       if BlockOn and (SameCellPos(P, CurrPos)) then
  2234.         begin
  2235.           if not DisplayErrorColor then
  2236.             FStringSituationColor := GetColor(13)
  2237.           else
  2238.             FStringSituationColor := GetColor(21);
  2239.         end {...if BlockOn and (SameCellPos(P, CurrPos)) }
  2240.       else if SameCellPos(P, CurrPos) then
  2241.         begin
  2242.           if not DisplayErrorColor then
  2243.             FStringSituationColor := GetColor(12)
  2244.           else
  2245.             FStringSituationColor := GetColor(20);
  2246.         end {...else if SameCellPos(P, CurrPos) }
  2247.       else if BlockOn and (CurrBlock^.CellInBlock(P)) then
  2248.         begin
  2249.           if not DisplayErrorColor then
  2250.             FStringSituationColor := GetColor(11)
  2251.           else
  2252.             FStringSituationColor := GetColor(19);
  2253.         end {...else if BlockOn and (CurrBlock^.CellInBlock(P)) }
  2254.       else
  2255.         if not DisplayErrorColor then
  2256.           ColorFound := False
  2257.         else
  2258.           FStringSituationColor := GetColor(18);
  2259.     end {...if not SheetProtected or ... }
  2260.   else
  2261.     begin
  2262.       if BlockOn and (SameCellPos(P, CurrPos)) then
  2263.         begin
  2264.           if not DisplayErrorColor then
  2265.             FStringSituationColor := GetColor(17)
  2266.           else
  2267.             FStringSituationColor := GetColor(25);
  2268.         end {...if BlockOn and (SameCellPos(P, CurrPos)) }
  2269.       else if SameCellPos(P, CurrPos) then
  2270.         begin
  2271.           if not DisplayErrorColor then
  2272.             FStringSituationColor := GetColor(16)
  2273.           else
  2274.             FStringSituationColor := GetColor(24);
  2275.         end {...else if SameCellPos(P, CurrPos) }
  2276.       else if BlockOn and (CurrBlock^.CellInBlock(P)) then
  2277.         begin
  2278.           if not DisplayErrorColor then
  2279.             FStringSituationColor := GetColor(15)
  2280.           else
  2281.             FStringSituationColor := GetColor(23);
  2282.         end {...else if BlockOn and (CurrBlock^.CellInBlock(P)) }
  2283.       else
  2284.          if not DisplayErrorColor then
  2285.            FStringSituationColor := GetColor(14)
  2286.          else
  2287.            FStringSituationColor := GetColor(22);
  2288.     end; {...if/else }
  2289. end; {...TSpreadSheet.FStringSituationColor }
  2290.  
  2291.  
  2292. procedure TSpreadSheet.FormatCells;
  2293. var
  2294.   Cancel, ValidFormat : Boolean;
  2295.   NewDecimalPlaces : Byte;
  2296.   Start, Stop : CellPos;
  2297.   NewCurrency: Char;
  2298.   F : FormatType;
  2299.   Code : Integer;
  2300.   Dialog : PDialog;
  2301.   ErrorString : String;
  2302.   Block: TBlock;
  2303. const
  2304.   CurrencyBit = $01;
  2305.   CommasBit = $02;
  2306.  
  2307.     procedure SetDialogFormatRec;
  2308.     { Determines the initial values for the format dialog's fields }
  2309.     var
  2310.       CellPtr : PCell;
  2311.     begin
  2312.       CellPtr := CellHash.Search(CurrPos);
  2313.       if CellPtr <> Empty then
  2314.         begin
  2315.           F := CellPtr^.Format(FormatHash, DisplayFormulas);
  2316.           with RFormat do
  2317.           begin
  2318.             NumberFormat := 0;
  2319.             Justification := (F shr JustShift) and JustPart;
  2320.             if (F and CurrencyPart) <> 0 then
  2321.               NumberFormat := NumberFormat or CurrencyBit;
  2322.             if (F and CommasPart) <> 0 then
  2323.               NumberFormat := NumberFormat or CommasBit;
  2324.             if ((F and DecPlacesPart) = 0) and
  2325.                not ((CellPtr^.CellType = ClValue) or ((CellPtr^.CellType =
  2326.                ClFormula)) and DisplayFormulas = True) then
  2327.               Str(DefaultDecimalPlaces, DecimalPlaces)
  2328.             else
  2329.               Str(F and DecPlacesPart, DecimalPlaces);
  2330.             if (F and CurrencyCharPart) <> 0 then
  2331.               CurrencyChar := Char((F and CurrencyCharPart) shr CurrencyShift)
  2332.             else
  2333.               CurrencyChar := Copy(DefaultCurrency, 2, 1);
  2334.           end; {...with RFormat }
  2335.         end {...if CellPtr <> Empty }
  2336.       else
  2337.         begin
  2338.           with RFormat do
  2339.           begin
  2340.             Justification := Ord(JLeft);
  2341.             NumberFormat := 0;
  2342.             Str(DefaultDecimalPlaces, DecimalPlaces);
  2343.             CurrencyChar := Copy(DefaultCurrency, 2, 1);
  2344.           end; {...with RFormat }
  2345.         end; {...if/else }
  2346.     end; {...SetDialogFormatRec }
  2347.  
  2348.     procedure GetValidFormat(Dialog: PDialog; var ValidFormat, Cancel: Boolean);
  2349.     { Returns ValidFormat as true is a valid format was entered }
  2350.     var
  2351.       SelectedCommand : Word;
  2352.     begin
  2353.       SelectedCommand := Desktop^.ExecView(Dialog);
  2354.       if SelectedCommand <> cmCancel then
  2355.         begin
  2356.           Dialog^.GetData(RFormat);
  2357.           val(RFormat.DecimalPlaces, NewDecimalPlaces, Code);
  2358.           if (NewDecimalPlaces > MaxDecimalPlaces) then
  2359.             ErrorString := ErrorString + GLStringList^.Get(sFormatError1Msg)
  2360.           else
  2361.             ValidFormat := True;
  2362.           if ((RFormat.NumberFormat and CurrencyBit) <> 0) then
  2363.           begin
  2364.             if not ((RFormat.CurrencyChar <> '') and
  2365.                (RFormat.CurrencyChar <> ' ')) then
  2366.             begin
  2367.               ErrorString := ErrorString +
  2368.                 GLStringList^.Get(sFormatError2Msg);
  2369.               ValidFormat := False;
  2370.             end; {...if not ((RFormat.CurrencyChar<>'') and... }
  2371.           end; {...if (RFormat.NumberFormat and CurrencyBit) <> 0) }
  2372.         end {...if SelectedCommand <> cmCancel }
  2373.       else
  2374.         begin
  2375.           Cancel := True;
  2376.           ValidFormat := True;
  2377.         end; {...if/else }
  2378.     end; {...GetValidFormat }
  2379.  
  2380. begin
  2381.   Cancel := False;
  2382.   ValidFormat := False;
  2383.   if BlockOn then
  2384.     begin
  2385.       Block.Start := CurrBlock^.Start;
  2386.       Block.Stop := CurrBlock^.Stop;
  2387.     end {...if BlockOn }
  2388.   else
  2389.     Block.Init(CurrPos);
  2390.   Dialog := PDialog(GLResFile^.Get('FormatDialog'));
  2391.   SetDialogFormatRec;
  2392.   Dialog^.SetData(RFormat);
  2393.   repeat
  2394.     ErrorString := GLStringList^.Get(sFormatErrorMsg);
  2395.     if (Application^.ValidView(Dialog) <> nil) then
  2396.       GetValidFormat(Dialog, ValidFormat, Cancel)
  2397.     else
  2398.       Exit;
  2399.     if not ValidFormat then
  2400.       MessageBox(ErrorString, nil, mfError+mfOkButton);
  2401.   until Cancel or ValidFormat;
  2402.   if not Cancel then
  2403.   begin
  2404.     Dialog^.GetData(RFormat);
  2405.     with RFormat do
  2406.     begin
  2407.       NewCurrency := CurrencyChar[1];
  2408.       SetFormat(Block, NewDecimalPlaces, Justification, NumberFormat, NewCurrency);
  2409.     end; { with }
  2410.     SetChanged(ModifiedYes);
  2411.     Block.Stop := ScreenBlock^.Stop;
  2412.     DisplayBlock(Block);
  2413.   end; {...else if not Cancel }
  2414.   Dispose(Dialog, Done);
  2415. end; {...TSpreadSheet.FormatCells }
  2416.  
  2417.  
  2418. {****************************************************************************}
  2419. { TSpreadSheet.GetNumber                                                     }
  2420. {****************************************************************************}
  2421. function TSpreadSheet.GetNumber: Integer;
  2422. begin
  2423.   GetNumber := PWindow(Owner)^.Number;
  2424. end;
  2425.  
  2426. {****************************************************************************}
  2427. { TSpreadSheet.GetPalette                                                    }
  2428. {****************************************************************************}
  2429. function TSpreadSheet.GetPalette: PPalette;
  2430. const
  2431.   CPalette : string[Length(CSpreadSheet)] = CSpreadSheet;
  2432. begin
  2433.   GetPalette := @CPalette;
  2434. end;
  2435.  
  2436. {****************************************************************************}
  2437. { TSpreadSheet.GoToCell                                                      }
  2438. {****************************************************************************}
  2439. procedure TSpreadSheet.GoToCell;
  2440. { Moves the highlight cursor to a user defined cell }
  2441. var
  2442.   Cancel, CellEntered : Boolean;
  2443.   OldPos, Pos : CellPos;
  2444.   Dialog : PDialog;
  2445.   FormLen : Word;
  2446. begin
  2447.   Cancel := False;
  2448.   CellEntered := False;
  2449.   Dialog := PDialog(GLResFile^.Get('GoToDialog'));
  2450.   repeat
  2451.     if (Application^.ValidView(Dialog) <> nil) then
  2452.       begin
  2453.         if Desktop^.ExecView(Dialog) <> cmCancel then
  2454.           begin
  2455.             Dialog^.GetData(RGoToCell);
  2456.             if not FormulaStart(RGoToCell.NewCell, 1, MaxCols, MaxRows, Pos,
  2457.                FormLen) then
  2458.               MessageBox(GLStringList^.Get(sInvalidCellMsg), nil, mfError +
  2459.                 mfOKButton)
  2460.             else
  2461.               CellEntered := True;
  2462.           end {...if Desktop^.ExecView(Dialog) <> cmCancel }
  2463.         else
  2464.           Cancel := True;
  2465.       end {...if Application^.ValidView(Dialog) <> nil }
  2466.     else
  2467.       Exit;
  2468.   until CellEntered or Cancel;
  2469.   if not Cancel then
  2470.     GoToPos(Pos);
  2471.   Dispose(Dialog, Done);
  2472. end;
  2473.  
  2474. {****************************************************************************}
  2475. { TSpreadSheet.GoToPos                                                       }
  2476. {****************************************************************************}
  2477. procedure TSpreadSheet.GoToPos(Pos: CellPos);
  2478. var
  2479.   OldPos : CellPos;
  2480. begin
  2481.   if not ScreenBlock^.CellInBlock(Pos) then
  2482.     begin
  2483.       CurrPos := Pos;
  2484.       ExtendCurrBlock(RedrawYes);
  2485.       SetScreenColStart(CurrPos.Col);
  2486.       SetScreenRowStart(CurrPos.Row);
  2487.       HScrollBar^.Value := ScreenBlock^.Start.Col;
  2488.       VScrollBar^.Value := ScreenBlock^.Start.Row;
  2489.       HScrollBar^.DrawView;
  2490.       VScrollBar^.DrawView;
  2491.       DrawView;
  2492.     end { if }
  2493.   else
  2494.     begin
  2495.       OldPos := CurrPos;
  2496.       CurrPos := Pos;
  2497.       MoveCell(OldPos);
  2498.     end; { else }
  2499. end;
  2500.  
  2501. {****************************************************************************}
  2502. { TSpreadSheet.HandleEvent                                                   }
  2503. {****************************************************************************}
  2504. procedure TSpreadSheet.HandleEvent(var Event: TEvent);
  2505. { Handles all spreadsheet related events }
  2506.  
  2507.   procedure CheckforClipBoardClose;
  2508.   { if the spreadsheet being closed is @self, it resets the clipboard }
  2509.   begin
  2510.     if ClipBoard.Active and (ClipBoard.SourceSpreadSheet = @Self) then
  2511.       ToggleClipBoardOff;
  2512.   end; {...CheckforClipBoardClose }
  2513.  
  2514.   procedure EscPressed;
  2515.   begin
  2516.     if BlockOn then
  2517.     begin
  2518.       ClearCurrBlock;
  2519.       DisplayCellData;
  2520.     end; {...if BlockOn }
  2521.     if ClipBoard.Active then
  2522.       ToggleClipBoardOff;
  2523.   end; {...EscPressed }
  2524.  
  2525. begin
  2526.   case Event.What of
  2527.     evKeyDown :
  2528.       begin
  2529.         if ClipBoard.Active and ((Event.KeyCode = kbDel) or
  2530.            (Event.CharCode in [Chr(32)..Chr(255)])) then
  2531.           ToggleClipBoardOff;
  2532.         KeyPressed := True;
  2533.         case Event.KeyCode of
  2534.           kbCtrlLeft  : MovePgLeft;
  2535.           kbCtrlRight : MovePgRight;
  2536.           kbDel       : EraseCellBlock(RemoveSingleCell);
  2537.           kbDown      : MoveDown;
  2538.           kbEnd       : ToggleEnd;
  2539.           kbEnter     : if Clipboard.Active then
  2540.                           PasteCellBlock;
  2541.           kbEsc       : EscPressed;
  2542.           kbHome      : MoveHome;
  2543.           KbLeft      : MoveLeft;
  2544.           kbPgDn      : MovePgDown;
  2545.           kbPgUp      : MovePgUp;
  2546.           kbRight     : MoveRight;
  2547.           kbUp        : MoveUp;
  2548.         end; {...case Event.KeyCode }
  2549.         KeyPressed := False;
  2550.         if Event.CharCode in [Chr(32)..Chr(255)] then
  2551.            HandleInput(Event.CharCode, EditNo);
  2552.       end; {...case Event.What of evKeyDown }
  2553.     evMouseDown :
  2554.       begin
  2555.         if Event.Double then
  2556.           SetNameWithMouse(Event)
  2557.         else if not SelectColumn(Event) then
  2558.           begin
  2559.             LocateCursorWithMouse(Event);
  2560.             while MouseEvent(Event, evMouseMove + evMouseAuto) do
  2561.             begin
  2562.               Desktop^.Lock;
  2563.               DragCursorWithMouse(Event);
  2564.               Desktop^.Unlock;
  2565.             end; {...while MouseEvent(Event, evMouseMove + evMouseAuto) }
  2566.           end; {...else if not SelectColumn(Event) }
  2567.       end; {...case Event.What of evMouseDown }
  2568.  
  2569.     evCommand:
  2570.       begin
  2571.         if ClipBoard.Active and not (Event.Command in [cmNewSheet, cmPaste,
  2572.            cmNext, cmPrev, cmZoom, cmResize, cmClose]) then
  2573.           ToggleClipBoardOff;
  2574.         case Event.Command of
  2575.           cmCut              : MoveCellBlock;
  2576.           cmPaste            : PasteCellBlock;
  2577.           cmClose            : CheckforClipBoardClose;
  2578.           cmCopy             : CopyCellBlock;
  2579.           cmClear            : EraseCellBlock(RemoveBlock);
  2580.           cmPrintSheet       : Print;
  2581.           cmChangeColWidth   : ChangeColWidth;
  2582.           cmDeleteColumns    : DeleteColumns;
  2583.           cmDeleteRows       : DeleteRows;
  2584.           cmInsertColumns    : InsertColumns;
  2585.           cmInsertRows       : InsertRows;
  2586.           cmEditCell         : HandleInput('', EditYes);
  2587.           cmFormatCells      : FormatCells;
  2588.           cmFormatDefault    : FormatDefault;
  2589.           cmGoToCell         : GoToCell;
  2590.           cmRecalc           : Recalc(DisplayYes);
  2591.           cmToggleAutoCalc   : ToggleAutoCalc;
  2592.           cmToggleFormulas   : ToggleFormulaDisplay;
  2593.           cmChangeColHeaders : ChangeColHeaders;
  2594.           cmDeleteColHeaders :
  2595.             begin
  2596.               DeleteColHeaders(CurrBlock);
  2597.               DisplayCols;
  2598.             end; {...case Event.Command of cmDeleteColHeaders }
  2599.           cmToggleHeaders    : ToggleDisplayHeaders;
  2600.           cmToggleProtection :
  2601.             begin
  2602.               SetProtection(not SheetProtected, True);
  2603.               SetChanged(ModifiedYes);
  2604.             end;
  2605.           cmSetLocked        : SetLocked;
  2606.           cmSetUnlocked      : SetUnlocked;
  2607.           cmSortData         : SortData;
  2608.         end; {...case Event.Command }
  2609.     end; {...case Event.What of evCommand }
  2610.   end; {...case Event.What }
  2611.   TScroller.HandleEvent(Event);
  2612. end; {...TSpreadSheet.HandleEvent }
  2613.  
  2614.  
  2615. procedure TSpreadSheet.HandleInput(FirstChar: String; Editing: Boolean);
  2616. { Gets data from the user, validates it and creates the corresponding cell }
  2617. var
  2618.   Deleted, FirstEdit, Good : Boolean;
  2619.   CurrentPos  : CellPos;
  2620.   CellValue : Extended;
  2621.   Code : Integer;
  2622.   InputLine   : PSheetInputLine;
  2623.   StringInput : PString;
  2624.   R : TRect;
  2625.  
  2626.     procedure DisplayEnteredString;
  2627.     var
  2628.       B : TDrawBuffer;
  2629.     begin
  2630.       with ContentsArea do
  2631.       begin
  2632.         MoveChar(B, ' ', Attrib, ScreenCols);
  2633.         Writeline(UpperLeft.Col, UpperLeft.Row, ScreenCols, 1, B);
  2634.         MoveStr(B, Copy(StringInput^, Succ(InputLine^.FirstPos),
  2635.           Min((Length(StringInput^) - InputLine^.FirstPos), ScreenCols)),
  2636.           Attrib);
  2637.         Writeline (Succ(UpperLeft.Col), UpperLeft.Row,
  2638.           Min((Length(StringInput^) - InputLine^.FirstPos), ScreenCols), 1, B);
  2639.       end; {...with ContenstArea }
  2640.     end; {...DisplayEnteredString }
  2641.  
  2642. begin
  2643.   if not SheetProtected or (SheetProtected and
  2644.      UnlockedHash.Search(CurrPos)) then
  2645.     begin
  2646.       Good := True;
  2647.       if TrackCursor then
  2648.         UpdateScreenBlockDisplay;
  2649.       GetMem(StringInput, 255);
  2650.       if StringInput = nil then
  2651.       begin
  2652.         Application^.OutofMemory;
  2653.         Exit;
  2654.       end; {...if StringInput = nil }
  2655.       GoToEnd := True;
  2656.       ToggleEnd;
  2657.       with ContentsArea do
  2658.       begin
  2659.         R.Assign(Succ(UpperLeft.Col), Succ(UpperLeft.Row),
  2660.           Succ(LowerRight.Col), Succ(LowerRight.Row));
  2661.         Inc(R.B.X);
  2662.         Inc(R.B.Y);
  2663.         if Editing then
  2664.         begin
  2665.           CellHash.Search(CurrPos)^.EditString(MaxDecimalPlaces, StringInput^);
  2666.           FirstChar := StringInput^;
  2667.         end; {...if Editing }
  2668.         InputLine := PSheetInputLine(GLResFile^.Get('InputLine'));
  2669.         InputLine^.SetBounds(R);
  2670.         if Editing then
  2671.           InputLine^.SetData(FirstChar)
  2672.         else
  2673.           begin
  2674.             InputLine^.Data^ := FirstChar;
  2675.             Inc(InputLine^.CurPos);
  2676.           end; {...if/else }
  2677.         FirstEdit := True;
  2678.         Parser^.Init(@CellHash, StringInput, MaxCols, MaxRows);
  2679.         repeat
  2680.           if FirstEdit then
  2681.             Owner^.ExecView(InputLine)
  2682.           else
  2683.             begin
  2684.               InputLine^.CurPos := Pred(Parser^.Position);
  2685.               if InputLine^.CurPos < (InputLine^.Size.X - 2) then
  2686.                 InputLine^.FirstPos := 0
  2687.               else
  2688.                 InputLine^.FirstPos := Succ(InputLine^.CurPos -
  2689.                   (InputLine^.Size.X - 2));
  2690.               Owner^.ExecView(InputLine);
  2691.             end; {...if/else }
  2692.           InputLine^.GetData(StringInput^);
  2693.           if Length(StringInput^) > 0 then
  2694.           begin
  2695.             DisplayEnteredString;
  2696.             Parser^.Parse;
  2697.             if Parser^.TokenError = 0 then
  2698.             begin
  2699.               if DoBeforeAddingCell then
  2700.                 begin
  2701.                   DeleteCell(CurrPos, Deleted);
  2702.                   if Parser^.CType = ClFormula then
  2703.                     Parser^.Inp^ := UpperCase(Parser^.Inp^);
  2704.                   Good := AddCell (Parser^.CType, CurrPos, Parser^.ParseError,
  2705.                     Parser^.ParseValue, Parser^.Inp^);
  2706.                 end { if }
  2707.               else
  2708.                   Parser^.TokenError := 1;
  2709.             end; {...if Parser^.TokenError = 0 }
  2710.           end; {...if Length(StringInput^) > 0 }
  2711.           FirstEdit := False;
  2712.         until (Length(StringInput^) = 0) or (Parser^.TokenError = 0) or
  2713.           not Good;
  2714.         if (Length(StringInput^) > 0) and Good then
  2715.         begin
  2716.           SetChanged(ModifiedYes);
  2717.           if AutoCalc then
  2718.             Recalc(DisplayYes);
  2719.           CurrentPos := CurrPos;
  2720.           DoAfterAddingCell;
  2721.           for CurrentPos.Col := CurrPos.Col to ScreenBlock^.Stop.Col do
  2722.             DisplayCell(CurrentPos);
  2723.         end; {...if (Length(StringInput^) > 0) and Good }
  2724.       end; {...with ContentsArea }
  2725.       Dispose(InputLine, Done);
  2726.       FreeMem(StringInput, 255);
  2727.       DisplayCellData;
  2728.     end {...if not SheetProtected or ... }
  2729.   else
  2730.     MessageBox(GLStringList^.Get(sCellsProtectedMsg), nil, mfInformation +
  2731.       mfOKButton);
  2732. end; {...TSpreadSheet.HandleInput }
  2733.  
  2734.  
  2735. procedure TSpreadSheet.InitCurrPos;
  2736. { Locates the cursor in the first column and in the first row }
  2737. begin
  2738.   CurrPos.Col := 1;
  2739.   CurrPos.Row := 1;
  2740. end; {...InitCurrPos }
  2741.  
  2742.  
  2743. {****************************************************************************}
  2744. { TSpreadSheet.InsertColToHash                                               }
  2745. {****************************************************************************}
  2746. procedure TSpreadSheet.InsertColToHash(Block: TBlock; Columns, StartInsCol:
  2747.   Word);
  2748. { Updates all the hash tables after a column or group of columns is inserted }
  2749. var
  2750.   Pos, Start, Stop : CellPos;
  2751.   Deleted : Boolean;
  2752.   F : File;
  2753.   H : HashItemPtr;
  2754.   CellPtr : PCell;
  2755.   Col : Word;
  2756. begin
  2757.   SetChanged(ModifiedYes);
  2758.   DeleteBlock(Block, Deleted);
  2759.   with CellHash do
  2760.   begin
  2761.     CellPtr := FirstItem;
  2762.     while CellPtr <> nil do
  2763.     begin
  2764.       with CellPtr^ do
  2765.       begin
  2766.         if (CellPtr^.ShouldUpdate) then
  2767.           FixFormulaCol(CellPtr, opInsert, StartInsCol, Columns, MaxCols,
  2768.             MaxRows);
  2769.       end; {...with CellPtr^ }
  2770.       CellPtr := NextItem;
  2771.     end; {...while CellPtr <> nil }
  2772.   end; {...with CellHash }
  2773.  
  2774.   for Col := (MaxCols - Pred(Columns)) to MaxCols do
  2775.     WidthHash.Delete(Col);
  2776.   with WidthHash do
  2777.   begin
  2778.     H := FirstItem;
  2779.     while H <> nil do
  2780.     begin
  2781.       if WordPTr(@H^.Data)^ >= StartInsCol then
  2782.         Inc(WordPtr(@H^.Data)^, Columns);
  2783.       H := NextItem;
  2784.     end; {...with H <> nil }
  2785.   end; {...with WidthHash }
  2786.  
  2787.   Stop.Col := Block.Stop.Col;
  2788.   Stop.Row := MaxInt;
  2789.   FormatHash.Delete(Block.Start, Stop);
  2790.   with FormatHash do
  2791.   begin
  2792.     H := FirstItem;
  2793.     while H <> nil do
  2794.     begin
  2795.       Move(H^.Data, Start, SizeOf(Start));
  2796.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2797.       if Start.Col >= StartInsCol then
  2798.       begin
  2799.         Inc(Start.Col, Columns);
  2800.         Move(Start, H^.Data, Sizeof(Start));
  2801.       end; {...if Start.Col >= StartInsCol }
  2802.       if Stop.Col >= StartInsCol then
  2803.       begin
  2804.         Inc(Stop.Col, Columns);
  2805.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2806.       end; {...if Stop.Col >= StartInsCol }
  2807.       H := NextItem;
  2808.     end; {...while H <> nil }
  2809.   end; {...with FormatHash }
  2810.  
  2811.   DeleteColHeaders(@Block);
  2812.   with ColHeadersHash do
  2813.   begin
  2814.     for Col := (MaxCols - Pred(Columns)) to MaxCols do
  2815.       Delete(Col);
  2816.     H := FirstItem;
  2817.     while H <> nil do
  2818.     begin
  2819.       if WordPTr(@H^.Data)^ >= StartInsCol then
  2820.         Inc(WordPtr(@H^.Data)^, Columns);
  2821.        H := NextItem;
  2822.     end; {...with H <> nil }
  2823.   end; {...with ColHeadersHash }
  2824.  
  2825.   Stop.Col := Block.Stop.Col;
  2826.   Stop.Row := MaxInt;
  2827.   UnlockedHash.Delete(Block.Start, Stop);
  2828.   with UnlockedHash do
  2829.   begin
  2830.     H := FirstItem;
  2831.     while H <> nil do
  2832.     begin
  2833.       Move(H^.Data, Start, SizeOf(Start));
  2834.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2835.       if Start.Col >= StartInsCol then
  2836.       begin
  2837.         Inc(Start.Col, Columns);
  2838.         Move(Start, H^.Data, Sizeof(Start));
  2839.       end; {...if Start.Col >= StartInsCol }
  2840.       if Stop.Col >= StartInsCol then
  2841.       begin
  2842.         Inc(Stop.Col, Columns);
  2843.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2844.       end; {...if Stop.Col >= StartInsCol }
  2845.       H := NextItem;
  2846.     end; {...while H <> nil }
  2847.   end; {...with UnlockedHash }
  2848.  
  2849.   StoreTablesToTempFile;
  2850.   DoneHashTables;
  2851.   Pos.Col := StartInsCol;
  2852.   Pos.Row := 0;
  2853.   LoadTablesFromTempFile(Pos, 0, Columns);
  2854.   Assign(F, GLStringList^.Get(sTempFileName));
  2855.   Erase(F);
  2856.   LastPos.Col := Min(LastPos.Col + Columns, MaxCols);
  2857.   if LastPos.Col = MaxCols then
  2858.     Pos.Col := MaxCols
  2859.   else
  2860.     begin
  2861.       if BlockOn then
  2862.         Pos.Col := Pred(StartInsCol + Columns) + Columns
  2863.       else
  2864.         Pos.Col := StartInsCol + Columns;
  2865.     end; {...if/else }
  2866.   if Deleted then
  2867.     Pos.Row := LastPos.Row
  2868.   else
  2869.     Pos.Row := 1;
  2870.   FindLastPos(Pos);
  2871.   FixOverWrite;
  2872. end; {...TSpreadSheet.InsertColToHash }
  2873.  
  2874.  
  2875. procedure TSpreadSheet.InsertColumns;
  2876. { Inserts one or more columns at the current position }
  2877. var
  2878.   Start, Stop: CellPos;
  2879.   H : HashItemPtr;
  2880.   CellPtr : PCell;
  2881.   Block : TBlock;
  2882.   Column, Columns, StartInsCol : Word;
  2883. begin
  2884.   Block.Start.Col := 0;
  2885.   Block.Start.Row := 0;
  2886.   Block.Stop.Col := 0;
  2887.   Block.Stop.Row := 0;
  2888.   if BlockOn then
  2889.     begin
  2890.       Columns := Succ(CurrBlock^.Stop.Col - CurrBlock^.Start.Col);
  2891.       StartInsCol := CurrBlock^.Start.Col;
  2892.       if Pred(LastPos.Col + Columns) >= MaxCols then
  2893.       begin
  2894.         with Block do
  2895.         begin
  2896.           Start.Col := MaxCols - Pred(Columns);
  2897.           Start.Row := 1;
  2898.           Stop.Col := MaxCols;
  2899.           Stop.Row := LastPos.Row;
  2900.         end; {...with Block }
  2901.         LastPos.Col := MaxCols;
  2902.       end {...if Pred(LastPos.Col + Columns) >= MaxCols }
  2903.     end {...if BlockOn }
  2904.   else
  2905.     begin
  2906.       Columns := 1;
  2907.       StartInsCol := CurrPos.Col;
  2908.       if LastPos.Col = MaxCols then
  2909.       begin
  2910.         with Block do
  2911.         begin
  2912.           Start.Col := MaxCols;
  2913.           Start.Row := 1;
  2914.           Stop.Col := MaxCols;
  2915.           Stop.Row := LastPos.Row;
  2916.         end; {...with Block do }
  2917.       end {...if LastPos.Col = MaxCols }
  2918.     end; {...if/else }
  2919.   MessageDialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  2920.   if Application^.ValidView(MessageDialog) <> nil then
  2921.     Desktop^.Insert(MessageDialog)
  2922.   else
  2923.     begin
  2924.       MessageDialog := nil;
  2925.       Exit;
  2926.     end; { else }
  2927.   InsertColToHash(Block, Columns, StartInsCol);
  2928.   SetScreenColStart(ScreenBlock^.Start.Col);
  2929.   if AutoCalc then
  2930.     Recalc(DisplayNo);
  2931.   if MessageDialog <> nil then
  2932.   begin
  2933.     Desktop^.Delete(MessageDialog);
  2934.     Dispose(MessageDialog, Done);
  2935.     MessageDialog := nil;
  2936.   end; { if }
  2937.   DrawView;
  2938. end; {...TSpreadSheet.InsertColumns }
  2939.  
  2940.  
  2941. {****************************************************************************}
  2942. { TSpreadSheet.InsertRowToHash                                               }
  2943. {****************************************************************************}
  2944. procedure TSpreadSheet.InsertRowToHash(Block: TBlock; Rows, StartInsRow:
  2945.   Word);
  2946. { Updates all the hash tables after a row or group of rows is deleted }
  2947. var
  2948.   Pos, Start, Stop : CellPos;
  2949.   Deleted : Boolean;
  2950.   F : File;
  2951.   H : HashItemPtr;
  2952.   CellPtr : PCell;
  2953. begin
  2954.   SetChanged(ModifiedYes);
  2955.   DeleteBlock(Block, Deleted);
  2956.   with CellHash do
  2957.   begin
  2958.     CellPtr := FirstItem;
  2959.     while CellPtr <> nil do
  2960.     begin
  2961.       with CellPtr^ do
  2962.       begin
  2963.         if (CellPtr^.ShouldUpdate) then
  2964.           FixFormulaRow(CellPtr, opInsert, StartInsRow, Rows, MaxCols,
  2965.             MaxRows);
  2966.       end; {...with CellPtr^ }
  2967.       CellPtr := NextItem;
  2968.     end; {...while CellPtr <> nil }
  2969.   end; {...with CellHash }
  2970.  
  2971.   Stop.Col := MaxInt;
  2972.   Stop.Row := Block.Stop.Row;
  2973.   FormatHash.Delete(Block.Start, Stop);;
  2974.   with FormatHash do
  2975.   begin
  2976.     H := FirstItem;
  2977.     while H <> nil do
  2978.     begin
  2979.       Move(H^.Data, Start, SizeOf(Start));
  2980.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2981.       if Start.Row >= StartInsRow then
  2982.       begin
  2983.         Inc(Start.Row, Rows);
  2984.         Move(Start, H^.Data, Sizeof(Start));
  2985.       end; {...if Start.Row >= StartInsRow }
  2986.       if Stop.Row >= StartInsRow then
  2987.       begin
  2988.         Inc(Stop.Row, Rows);
  2989.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2990.       end; {...if Stop.Row >= StartInsRow }
  2991.       H := NextItem;
  2992.     end; {...while H <> nil }
  2993.   end; {...with FormatHash }
  2994.  
  2995.   Stop.Col := MaxInt;
  2996.   Stop.Row := Block.Stop.Row;
  2997.   UnlockedHash.Delete(Block.Start, Stop);
  2998.   with UnlockedHash do
  2999.   begin
  3000.     H := FirstItem;
  3001.     while H <> nil do
  3002.     begin
  3003.       Move(H^.Data, Start, SizeOf(Start));
  3004.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  3005.       if Start.Row >= StartInsRow then
  3006.       begin
  3007.         Inc(Start.Row, Rows);
  3008.         Move(Start, H^.Data, Sizeof(Start));
  3009.       end; {...if Start.Row >= StartInsRow }
  3010.       if Stop.Row >= StartInsRow then
  3011.       begin
  3012.         Inc(Stop.Row, Rows);
  3013.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  3014.       end; {...if Stop.Row >= StartInsRow }
  3015.       H := NextItem;
  3016.     end; {...while H <> nil }
  3017.   end; {...with UnlockedHash }
  3018.  
  3019.   StoreTablesToTempFile;
  3020.   DoneHashTables;
  3021.   Pos.Col := 0;
  3022.   Pos.Row := StartInsRow;
  3023.   LoadTablesFromTempFile(Pos, Rows, 0);
  3024.   Assign(F, GLStringList^.Get(sTempFileName));
  3025.   Erase(F);
  3026.   if Pred(LastPos.Row+Rows) < MaxRows then
  3027.      LastPos.Row := Min(LastPos.Row + Rows, MaxRows);
  3028.   if LastPos.Row = MaxRows then
  3029.      Pos.Row := MaxRows
  3030.   else
  3031.     begin
  3032.       if BlockOn then
  3033.         Pos.Row := Pred(StartInsRow + Rows) + Rows
  3034.       else
  3035.         Pos.Row := StartInsRow + Rows;
  3036.     end; {...if/else }
  3037.   if Deleted then
  3038.     Pos.Col := LastPos.Col
  3039.   else
  3040.     Pos.Col := 1;
  3041.   FindLastPos(Pos);
  3042.   FixOverWrite;
  3043. end; {...TSpreadSheet.InsertRowToHash }
  3044.  
  3045.  
  3046. procedure TSpreadSheet.InsertRows;
  3047. { Inserts one or more rows at the current position }
  3048. var
  3049.   Start, Stop: CellPos;
  3050.   H : HashItemPtr;
  3051.   CellPtr : PCell;
  3052.   Block : TBlock;
  3053.   Rows, StartInsRow : Word;
  3054. begin
  3055.   Block.Start.Col := 0;
  3056.   Block.Start.Row := 0;
  3057.   Block.Stop.Col := 0;
  3058.   Block.Stop.Row := 0;
  3059.   if BlockOn then
  3060.     begin
  3061.       Rows := Succ(CurrBlock^.Stop.Row - CurrBlock^.Start.Row);
  3062.       StartInsRow := CurrBlock^.Start.Row;
  3063.       if Pred(LastPos.Row + Rows) >= MaxRows then
  3064.         begin
  3065.           with Block do
  3066.           begin
  3067.             Start.Col := 1;
  3068.             Start.Row := MaxRows - Pred(Rows);
  3069.             Stop.Col := LastPos.Col;
  3070.             Stop.Row := MaxRows;
  3071.           end; {...with Block }
  3072.           LastPos.Row := MaxRows;
  3073.         end {...if Pred(LastPos.Row + Rows) >= MaxRows }
  3074.     end {...if BlockOn }
  3075.   else
  3076.     begin
  3077.       Rows := 1;
  3078.       StartInsRow := CurrPos.Row;
  3079.       if LastPos.Row = MaxRows then
  3080.       begin
  3081.         with Block do
  3082.         begin
  3083.           Start.Col := 1;
  3084.           Start.Row := MaxRows;
  3085.           Stop.Col := LastPos.Col;
  3086.           Stop.Row := MaxRows;
  3087.         end; {...with Block }
  3088.       end {...if LastPos.Row = MaxRows }
  3089.     end; {...if/else }
  3090.   MessageDialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  3091.   if Application^.ValidView(MessageDialog) <> nil then
  3092.     Desktop^.Insert(MessageDialog)
  3093.   else
  3094.     begin
  3095.       MessageDialog := nil;
  3096.       Exit;
  3097.     end; { else }
  3098.   InsertRowToHash(Block, Rows, StartInsRow);
  3099.   if AutoCalc then
  3100.     Recalc(DisplayNo);
  3101.   if MessageDialog <> nil then
  3102.   begin
  3103.     Desktop^.Delete(MessageDialog);
  3104.     Dispose(MessageDialog, Done);
  3105.     MessageDialog := nil;
  3106.   end; { if }
  3107.   DrawView;
  3108. end; {...TSpreadSheet.InsertRows }
  3109.  
  3110.  
  3111. constructor TSpreadSheet.Load(var S: TStream);
  3112. { Loads the spreadsheet object from a stream }
  3113. var
  3114.   R : TRect;
  3115.   AdjustPos : CellPos;
  3116.   FileHeader : String[Length(OOGridFileHeader)];
  3117. const
  3118.   MinRowsToDisplay = 2;
  3119. begin
  3120.   AdjustPos.Col := 0;
  3121.   AdjustPos.Row := 0;
  3122.   TScroller.Load(S);
  3123.   S.Read(FileHeader, SizeOf(FileHeader));
  3124.   if FileHeader <> OOGridFileHeader then
  3125.   begin
  3126.     S.Error(stInvalidFormatError, 0);
  3127.     Exit;
  3128.   end; {...if FileHeader <> OOGridFileHeader }
  3129.   S.Read(EmptyRowsAtTop, SizeOf(EmptyRowsAtTop));
  3130.   S.Read(EmptyRowsAtBottom ,SizeOf(EmptyRowsAtBottom));
  3131.   S.Read(MaxCols, SizeOf(MaxCols));
  3132.   S.Read(MaxRows, SizeOf(MaxRows));
  3133.   S.Read(DefaultColWidth, SizeOf(DefaultColWidth));
  3134.   S.Read(DefaultDecimalPlaces, SizeOf(DefaultDecimalPlaces));
  3135.   S.Read(MaxDecimalPlaces, SizeOf(MaxDecimalPlaces));
  3136.   S.Read(DefaultCurrency, SizeOf(DefaultCurrency));
  3137.   S.Read(LastPos, SizeOf(LastPos));
  3138.   LoadHashTables(S, AdjustPos, 0, 0);
  3139.   if S.Status <> 0 then
  3140.     Exit;
  3141.   if not FixOverWrite then
  3142.   begin
  3143.     S.Error(stNoMemoryError, 0);
  3144.     Exit;
  3145.   end; {...if not FixOverWrite }
  3146.   ScreenBlock := PBlock(S.Get);
  3147.   S.Read(CurrPos, SizeOf(CurrPos));
  3148.   S.Read(BlockOn, SizeOf(BlockOn));
  3149.   CurrBlock := PBlock(S.Get);
  3150.   if S.Status <> 0 then
  3151.     Exit;
  3152.   S.Read(DisplayFormulas, SizeOf(DisplayFormulas));
  3153.   S.Read(AutoCalc, SizeOf(AutoCalc));
  3154.   S.Read(DisplayHeaders, SizeOf(DisplayHeaders));
  3155.   S.Read(SheetProtected, SizeOf(SheetProtected));
  3156.   if S.Status <> 0 then
  3157.     Exit;
  3158.   SetProtection(SheetProtected, False);
  3159.   RowNumberSpace := 6;
  3160.   MaxColWidth := ScreenCols - RowNumberSpace;
  3161.   MaxScreenCols := MaxColWidth div DefaultMinColWidth;
  3162.   GetMem(ColStart, MaxScreenCols);
  3163.   if ColStart = nil then
  3164.   begin
  3165.     S.Error(stNoMemoryError, 0);
  3166.     Exit;
  3167.   end; {...if ColStart = nil }
  3168.   OldCurrPos := CurrPos;
  3169.   GetExtent(R);
  3170.   Inc(R.A.Y, EmptyRowsAtTop);
  3171.   Dec(R.B.Y, EmptyRowsAtBottom);
  3172.   SetAreas(R);
  3173.   Recalc(DisplayNo);
  3174. end; {...TSpreadSheet.Load }
  3175.  
  3176.  
  3177. procedure TSpreadSheet.LoadDelimited(FileName: PathStr);
  3178. var
  3179.   F : Text;
  3180.   S, SAdd : String;
  3181.   V : Extended;
  3182.   Counter, Code : Integer;
  3183.   Pos : CellPos;
  3184.   NotString : Boolean;
  3185.   TempStream : TBufStream;
  3186.  
  3187. const
  3188.   CR = CHR(13);
  3189.   AL = CHR(10);
  3190.  
  3191.   procedure CloseAndUpdateHash;
  3192.   begin
  3193.     Close(F);
  3194.     FixOverWrite;
  3195.     FindLastPos(LastPos);
  3196.     DrawView;
  3197.     LowMemSize := 4096 div 16;
  3198.     TempStream.Done;
  3199.   end; {...CloseAndUpdateHash }
  3200.  
  3201. begin
  3202.   LowMemSize := 5088 div 16;
  3203.   TempStream.Init(GLStringList^.Get(sTempFileName), stCreate, 1024);
  3204.   Assign(F, FileName);
  3205.   Reset(F);
  3206.   Pos.Row := 0;
  3207.   NotString := True;
  3208.   while not Eof(F) do
  3209.   begin
  3210.     Readln(F, S);
  3211.     Pos.Col := 1;
  3212.     Inc(Pos.Row);
  3213.     SAdd := '';
  3214.     for Counter := 1 to Length(S) do
  3215.     begin
  3216.       if ( S[Counter] in [','] ) and NotString then
  3217.       begin
  3218.         if SAdd <> '' then
  3219.         begin
  3220.           case Pos.Col of
  3221.             2..10,15 :
  3222.               begin
  3223.                 if not AddCell(ClText, Pos, False, 0, ' '+SAdd) then
  3224.                 begin
  3225.                   CloseAndUpdateHash;
  3226.                   Exit;
  3227.                 end; {...if not AddCell }
  3228.               end; {...case Pos.Col of 2..10, 15] }
  3229.  
  3230.             1, 11..14, 16 :
  3231.               begin
  3232.                 if SAdd[Length(SAdd)] = ' ' then
  3233.                   SAdd := Copy(SAdd, 1, Length(SAdd)-1);
  3234.                 val(SAdd, V, Code);
  3235.                 if not AddCell(ClValue, Pos, False, V, '') then
  3236.                 begin
  3237.                   CloseAndUpdateHash;
  3238.                   Exit;
  3239.                 end; {...if not AddCell }
  3240.               end; {...case Pos.Col of 1, 11..14, 16 }
  3241.           end; {...case Pos.Col }
  3242.           SAdd := '';
  3243.         end; {...if SAdd <> '' }
  3244.         Inc(Pos.Col);
  3245.       end; {...if ( S[Counter] in ',' ) and NotString }
  3246.       if S[Counter] = '"' then
  3247.         NotString := not NotString;
  3248.       if not (S[Counter] in ['"','$',',']) then
  3249.         SAdd := SAdd + S[Counter];
  3250.     end; {...for Counter }
  3251.     if SAdd <> '' then
  3252.     begin
  3253.       val(SAdd, V, Code);
  3254.       if not AddCell(ClValue, Pos, False, V, '') then
  3255.       begin
  3256.         CloseAndUpdateHash;
  3257.         Exit;
  3258.       end; {...if not AddCell }
  3259.       SAdd := '';
  3260.     end; {...if SAdd <> '' }
  3261.   end; {...while not Eof(F) }
  3262.   CloseAndUpdateHash;
  3263. end; {...TSpreadSheet.LoadDelimited }
  3264.  
  3265.  
  3266. procedure TSpreadSheet.LoadHashTables(var S: TStream; AdjustAfter: CellPos;
  3267.   RowAdjustment, ColAdjustment: Integer);
  3268. { Loads all the hash tables from a stream }
  3269. var
  3270.   TotalC, TotalF : LongInt;
  3271.   TotalW : Word;
  3272.   TotalHeaders : Word;
  3273.   TotalUnlocked : LongInt;
  3274. begin
  3275.   S.Read(TotalC, SizeOf(TotalC));
  3276.   S.Read(TotalW, SizeOf(TotalW));
  3277.   S.Read(TotalF, SizeOf(TotalF));
  3278.   S.Read(TotalHeaders, 2);
  3279.   S.Read(TotalUnlocked, SizeOf(TotalUnlocked));
  3280.   if not CellHash.Init(CellHashStart(TotalC)) then
  3281.   begin
  3282.     S.Error(stNoMemoryError, 0);
  3283.     Exit;
  3284.   end; {...if not CellHash.Init(CellHashStart(TotalC)) }
  3285.   if not WidthHash.Init(WidthHashStart, DefaultColWidth) then
  3286.   begin
  3287.     S.Error(stNoMemoryError, 0);
  3288.     Exit;
  3289.   end; {...if not WidthHash.Init(WidthHashStart, DefaultColWidth) }
  3290.   if not FormatHash.Init then
  3291.   begin
  3292.     S.Error(stNoMemoryError, 0);
  3293.     Exit;
  3294.   end; {...if not FormatHash.Init }
  3295.   if not OverWriteHash.Init(OverWriteHashStart) then
  3296.   begin
  3297.     S.Error(stNoMemoryError, 0);
  3298.     Exit;
  3299.   end; {...if not OverwriteHash.Init(OverwriteHashStart) }
  3300.   if not ColHeadersHash.Init(ColHeadersHashStart) then
  3301.   begin
  3302.     S.Error(stNoMemoryError, 0);
  3303.     Exit;
  3304.   end; {...if not ColHeadersHash.Init(ColHeadersHashStart) }
  3305.   if not UnlockedHash.Init then
  3306.   begin
  3307.     S.Error(stNoMemoryError, 0);
  3308.     Exit;
  3309.   end; {...if not UnlockedHash.Init }
  3310.   CellHash.Load(S, TotalC, AdjustAfter, RowAdjustment, ColAdjustment);
  3311.   if S.Status <> 0 then
  3312.     Exit;
  3313.   WidthHash.Load(S, TotalW);
  3314.   if S.Status <> 0 then
  3315.     Exit;
  3316.   FormatHash.Load(S, TotalF);
  3317.   if S.Status <> 0 then
  3318.     Exit;
  3319.   ColHeadersHash.Load(S, TotalHeaders);
  3320.   if S.Status <> 0 then
  3321.     Exit;
  3322.   UnlockedHash.Load(S, TotalUnlocked);
  3323. end; {...TSpreadSheet.LoadHashTables }
  3324.  
  3325.  
  3326. procedure TSpreadSheet.LoadTablesFromTempFile(AdjustAfter: CellPos;
  3327.   RowAdjustment, ColAdjustment: Integer);
  3328. { Loads the hash tables from the temporal file in disk }
  3329. var
  3330.   S : TDosStream;
  3331. begin
  3332.   S.Init(GLStringList^.Get(sTempFileName), stOpenRead);
  3333.   LoadHashTables(S, AdjustAfter, RowAdjustment, ColAdjustment);
  3334.   S.Done;
  3335. end; {...TSpreadSheet.LoadTablesFromTempFile }
  3336.  
  3337.  
  3338. procedure TSpreadSheet.LocateCursorWithMouse(Event: TEvent);
  3339. { Positions the highlight cursor in the position where the mouse was clicked }
  3340. var
  3341.   ColScrPos : Byte;
  3342.   OldPos : CellPos;
  3343.   Counter : Integer;
  3344.   Mouse : TPoint;
  3345. begin
  3346.   MakeLocal(Event.Where, Mouse);
  3347.   with ScreenBlock^ do
  3348.   begin
  3349.     if DisplayArea.PointInArea(Mouse.X, Mouse.Y) then
  3350.     begin
  3351.       CheckforDragging;
  3352.       OldPos := CurrPos;
  3353.       CurrPos.Row := YToRow(Mouse.Y);
  3354.       if (not NoBlankArea) and (BlankArea.PointInArea(Mouse.X, Mouse.Y)) then
  3355.         begin
  3356.           CurrPos.Col := Min(Succ(Stop.Col), MaxCols);
  3357.           DisplayAllCells;
  3358.           DisplayCellData;
  3359.         end { if }
  3360.       else
  3361.         begin
  3362.           CurrPos.Col := XToCol(Mouse.X);
  3363.           MoveCell(OldPos);
  3364.         end; { else }
  3365.     end; {...if DisplayArea.PointInArea(Mouse.X, Mouse.Y) }
  3366.   end; {...with ScreenBlock^ }
  3367. end; {...TSpreadSheet.LocateCursorWithMouse }
  3368.  
  3369.  
  3370. procedure TSpreadSheet.MoveCell(OldPos: CellPos);
  3371. { Moves the cursor from one place to another and extends the block if active }
  3372. begin
  3373.   Desktop^.Lock;
  3374.   ExtendCurrBlock(RedrawYes);
  3375.   if ScreenBlock^.CellInBlock(OldPos) or
  3376.     (OldPos.Col = Succ(ScreenBlock^.Stop.Col)) then
  3377.     DisplayCell(OldPos);
  3378.   DisplayCell(CurrPos);
  3379.   DisplayCellData;
  3380.   Desktop^.Unlock;
  3381. end; {...TSpreadSheet.MoveCell}
  3382.  
  3383.  
  3384. procedure TSpreadSheet.MoveCellBlock;
  3385. { Activates the clipboard and sets it to indicate the block to be moved }
  3386. var
  3387.   Block : PBlock;
  3388. begin
  3389.   if BlockOn then
  3390.     begin
  3391.       if not CellsProtected(CurrBlock^) then
  3392.         begin
  3393.           New(Block, Init(CurrBlock^.Start));
  3394.           Block^.Stop := CurrBlock^.Stop;
  3395.           ToggleClipBoardOn(@Self, Block, BlockOn, opMove);
  3396.         end {...if not CellsProtected(CurrBlock^) }
  3397.       else
  3398.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), nil, mfInformation +
  3399.           mfOKButton);
  3400.     end {...if BlockOn}
  3401.   else
  3402.     begin
  3403.       if not SheetProtected or (SheetProtected and
  3404.          UnlockedHash.Search(CurrPos)) then
  3405.         begin
  3406.           New(Block, Init(CurrPos));
  3407.           Block^.Stop := CurrPos;
  3408.           ToggleClipBoardOn(@Self, Block, BlockOn, opMove);
  3409.         end {...if not SheetProtected or ... }
  3410.       else
  3411.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), nil, mfInformation +
  3412.           mfOKButton);
  3413.     end; {...if/else }
  3414. end;  {...TSpreadSheet.MoveCellBlock}
  3415.  
  3416.  
  3417. {****************************************************************************}
  3418. { TSpreadSheet.MoveDown                                                      }
  3419. {****************************************************************************}
  3420. procedure TSpreadSheet.MoveDown;
  3421. { Moves the cursor one row down }
  3422. var
  3423.   OldPos : CellPos;
  3424. begin
  3425.   if CurrPos.Row < MaxRows then
  3426.   begin
  3427.     CheckForDragging;
  3428.     Desktop^.Lock;
  3429.     OldPos := CurrPos;
  3430.     if GoToEnd then
  3431.       CurrPos.Row := MaxRows
  3432.     else
  3433.       Inc(CurrPos.Row);
  3434.     if TrackCursor then
  3435.       UpdateScreenBlockDisplay
  3436.     else
  3437.       MoveCell(OldPos);
  3438.     Desktop^.Unlock;
  3439.   end; { if }
  3440.   GoToEnd := True;
  3441.   ToggleEnd;
  3442. end;
  3443.  
  3444. {****************************************************************************}
  3445. { TSpreadSheet.MoveHome                                                      }
  3446. {****************************************************************************}
  3447. procedure TSpreadSheet.MoveHome;
  3448. { Moves the cursor to the upper left corner of the spreadsheet }
  3449. var
  3450.   OldPos : CellPos;
  3451. begin
  3452.   Desktop^.Lock;
  3453.   CheckforDragging;
  3454.   OldPos := CurrPos;
  3455.   InitCurrPos;
  3456.   if TrackCursor then
  3457.     UpdateScreenBlockDisplay
  3458.   else
  3459.     MoveCell(OldPos);
  3460.   GoToEnd := True;
  3461.   ToggleEnd;
  3462.   Desktop^.Unlock;
  3463. end;
  3464.  
  3465.  
  3466. {****************************************************************************}
  3467. { TSpreadSheet.MoveLeft                                                      }
  3468. {****************************************************************************}
  3469. procedure TSpreadSheet.MoveLeft;
  3470. { Moves the cursor one column left }
  3471. var
  3472.   OldPos : CellPos;
  3473. begin
  3474.   if CurrPos.Col > 1 then
  3475.   begin
  3476.     CheckForDragging;
  3477.     Desktop^.Lock;
  3478.     OldPos := CurrPos;
  3479.     if GoToEnd then
  3480.       CurrPos.Col := 1
  3481.     else
  3482.       Dec(CurrPos.Col);
  3483.     if TrackCursor then
  3484.       UpdateScreenBlockDisplay
  3485.     else
  3486.       MoveCell(OldPos);
  3487.     Desktop^.Unlock;
  3488.   end; { if }
  3489.   GoToEnd := True;
  3490.   ToggleEnd;
  3491. end;
  3492.  
  3493.  
  3494. procedure TSpreadSheet.MovePgDown;
  3495. { Moves the cursor one full page down }
  3496. var
  3497.   OldPos : CellPos;
  3498. begin
  3499.   if CurrPos.Row < MaxRows then
  3500.   begin
  3501.     CheckForDragging;
  3502.     Desktop^.Lock;
  3503.     OldPos := CurrPos;
  3504.     TrackCursor;
  3505.     CurrPos.Row := Min(MaxRows, CurrPos.Row + TotalRows);
  3506.     SetScreenRowStart(Min(MaxRows, Succ(ScreenBlock^.Stop.Row)));
  3507.     UpdateScreenBlockDisplay;
  3508.     Desktop^.Unlock;
  3509.   end; {...if CurrPos.Row < MaxRows }
  3510. end; {...TSpreadSheet.MovePgDown }
  3511.  
  3512.  
  3513. procedure TSpreadSheet.MovePgLeft;
  3514. { Moves the cursor one full page left }
  3515. var
  3516.   OldPos : CellPos;
  3517.   TotalCols : Byte;
  3518. begin
  3519.   if CurrPos.Col > 1 then
  3520.   begin
  3521.     CheckForDragging;
  3522.     Desktop^.Lock;
  3523.     OldPos := CurrPos;
  3524.     TotalCols := Succ(ScreenBlock^.Stop.Col - ScreenBlock^.Start.Col);
  3525.     SetScreenColStop(Max(1, Pred(ScreenBlock^.Start.Col)));
  3526.     CurrPos.Col := Max(ScreenBlock^.Start.Col, LongInt(CurrPos.Col) -
  3527.       TotalCols);
  3528.     UpdateScreenBlockDisplay;
  3529.     Desktop^.Unlock;
  3530.   end; {...if CurrPos.Col > 1 }
  3531. end; {...TSpreadSheet.MovePgLeft }
  3532.  
  3533.  
  3534. procedure TSpreadSheet.MovePgRight;
  3535. { Moves the cursor one full page right }
  3536. var
  3537.   OldPos : CellPos;
  3538.   TotalCols : Byte;
  3539. begin
  3540.   if CurrPos.Col < MaxCols then
  3541.   begin
  3542.     CheckForDragging;
  3543.     Desktop^.Lock;
  3544.     OldPos := CurrPos;
  3545.     TotalCols := Succ(ScreenBlock^.Stop.Col - ScreenBlock^.Start.Col);
  3546.     SetScreenColStart(Min(MaxCols, Succ(ScreenBlock^.Stop.Col)));
  3547.     CurrPos.Col := Min(ScreenBlock^.Stop.Col, LongInt(CurrPos.Col) +
  3548.       TotalCols);
  3549.     UpdateScreenBlockDisplay;
  3550.     Desktop^.Unlock;
  3551.   end; {...if CurrPos.Col < MaxCols }
  3552. end; {...TSpreadSheet.MovePgRight }
  3553.  
  3554.  
  3555. procedure TSpreadSheet.MovePgUp;
  3556. var
  3557.   OldPos, NewPos : CellPos;
  3558. begin
  3559.   if CurrPos.Row > 1 then
  3560.   begin
  3561.     CheckForDragging;
  3562.     Desktop^.Lock;
  3563.     OldPos := CurrPos;
  3564.     TrackCursor;
  3565.     CurrPos.Row := Max(1, LongInt(CurrPos.Row) - TotalRows);
  3566.     SetScreenRowStop(Max(1, Pred(ScreenBlock^.Start.Row)));
  3567.     UpdateScreenBlockDisplay;
  3568.     Desktop^.Unlock;
  3569.   end; {...if CurrPos.Row > 1 }
  3570. end; {...TSpreadSheet.MovePgUp }
  3571.  
  3572.  
  3573. {****************************************************************************}
  3574. { TSpreadSheet.MoveRight                                                     }
  3575. {****************************************************************************}
  3576. procedure TSpreadSheet.MoveRight;
  3577. { Moves the cursor one column to the right }
  3578. var
  3579.   OldPos : CellPos;
  3580. begin
  3581.   if CurrPos.Col < MaxCols then
  3582.   begin
  3583.     CheckForDragging;
  3584.     Desktop^.Lock;
  3585.     OldPos := CurrPos;
  3586.     if GoToEnd then
  3587.       CurrPos.Col := MaxCols
  3588.     else
  3589.       Inc(CurrPos.Col);
  3590.     if TrackCursor then
  3591.       UpdateScreenBlockDisplay
  3592.     else
  3593.       MoveCell(OldPos);
  3594.     Desktop^.Unlock;
  3595.   end; { if }
  3596.   GoToEnd := True;
  3597.   ToggleEnd;
  3598. end;
  3599.  
  3600. {****************************************************************************}
  3601. { TSpreadSheet.MoveUp                                                        }
  3602. {****************************************************************************}
  3603. procedure TSpreadSheet.MoveUp;
  3604. { Moves the cursor one row up }
  3605. var
  3606.   OldPos : CellPos;
  3607. begin
  3608.   if CurrPos.Row > 1 then
  3609.   begin
  3610.     CheckForDragging;
  3611.     Desktop^.Lock;
  3612.     OldPos := CurrPos;
  3613.     if GoToEnd then
  3614.       CurrPos.Row := 1
  3615.     else
  3616.       Dec(CurrPos.Row);
  3617.     if TrackCursor then
  3618.       UpdateScreenBlockDisplay
  3619.     else
  3620.       MoveCell(OldPos);
  3621.     Desktop^.Unlock;
  3622.   end; { if }
  3623.   GoToEnd := True;
  3624.   ToggleEnd;
  3625. end;
  3626.  
  3627. function TSpreadSheet.OverwriteHashStart: BucketRange;
  3628. { Returns the initial number of buckest for the OverwriteHash }
  3629. begin
  3630.   OverwriteHashStart := 10;
  3631. end; {...TSpreadSheet.OverwriteHashStart}
  3632.  
  3633.  
  3634. function TSpreadSheet.Parser: PParserObject;
  3635. { Returns a pointer to the parser to be used }
  3636. begin
  3637.   Parser := StandardParser;
  3638. end; {...TSpreadSheet.Parser }
  3639.  
  3640.  
  3641. procedure TSpreadSheet.PasteBlock(DestBlock: TBlock; Formulas: Word);
  3642. { Moves or copies a block of cells to a new position }
  3643. var
  3644.   Deleted, Good : Boolean;
  3645.   DestPos, SrcPos : CellPos;
  3646.   FormOp : FormulaOps;
  3647.   CellPtr, CP : PCell;
  3648.   ColChange, RowChange : ShortInt;
  3649.   SrcStartCol, DestStartCol : Word;
  3650. const
  3651.   CopyColLitBit = $01;
  3652.   CopyRowLitBit = $02;
  3653. begin
  3654.   Good := True;
  3655.   with ClipBoard do
  3656.   begin
  3657.     if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) then
  3658.       { A single cell will be copied to a block of cells }
  3659.       begin
  3660.         SrcPos := BlockToCopy^.Start;
  3661.         DestPos := DestBlock.Start;
  3662.  
  3663.         if DestBlock.CellInBlock(SrcPos) and
  3664.            (SourceSpreadSheet = @Self) then
  3665.           { if the source cell is in the destination block then
  3666.             delete it from the cell hash to avoid storing the same
  3667.             cell twice at the same position }
  3668.           CellHash.Delete(SrcPos, CellPtr)
  3669.         else
  3670.           CellPtr := SourceCellHash^.Search(SrcPos);
  3671.         if CellPtr <> Empty then
  3672.         begin
  3673.           SetChanged(ModifiedYes);
  3674.           while Good and (DestPos.Row <= DestBlock.Stop.Row) do
  3675.           begin
  3676.             DestPos.Col := DestBlock.Start.Col;
  3677.             while Good and (DestPos.Col <= DestBlock.Stop.Col) do
  3678.             begin
  3679.               with CellPtr^ do
  3680.               begin
  3681.                 { Delete the current cell in the destination position }
  3682.                 DeleteCell(DestPos, Deleted);
  3683.  
  3684.                 { Add a copy of the source cell in the new position }
  3685.                 Good := AddCell(CellType, DestPos, HasError, CurrValue,
  3686.                   CopyString);
  3687.                 if not Good then
  3688.                 begin
  3689.                   if DestBlock.CellInBlock(SrcPos) and
  3690.                      (SourceSpreadSheet = @Self) then
  3691.                   { if the cell was not added to the cell hash table
  3692.                     because of a low memory error, and the source cell was
  3693.                     in the destination block, then add the source cell
  3694.                     to the table at the destination position.  This can be
  3695.                     done because the source cell already has memory
  3696.                     allocated and it does not use more memory when added to
  3697.                     the hash table }
  3698.                   begin
  3699.                     CellPtr^.Loc := DestPos;
  3700.                     CellHash.Add(CellPtr)
  3701.                   end; { if }
  3702.                 end; { if }
  3703.  
  3704.                 { Determine if cell addresses in formulas should be modified }
  3705.                 CP := CellHash.Search(DestPos);
  3706.                 if (CP <> nil) and CP^.ShouldUpdate then
  3707.                 begin
  3708.                   if (Formulas and CopyColLitBit) = 0 then
  3709.                   { Formula column addresses must be modified }
  3710.                   begin
  3711.                     if DestPos.Col >= SrcPos.Col then
  3712.                       { The column addresses must be increased }
  3713.                       FormOp := opInsert
  3714.                     else
  3715.                       { The column addresses must be decreased }
  3716.                       FormOp := opDelete;
  3717.                     FixFormulaCol(CP, FormOp, 0, Abs(LongInt(DestPos.Col) -
  3718.                       LongInt(SrcPos.Col)), MaxCols, MaxRows);
  3719.                   end; {...if (Formulas and CopyColLitBit) = 0 }
  3720.                   if (Formulas and CopyRowLitBit) = 0 then
  3721.                   { Formula row addresses must be modified }
  3722.                   begin
  3723.                     if DestPos.Row >= SrcPos.Row then
  3724.                       { The row addresses must be increased }
  3725.                       FormOp := opInsert
  3726.                     else
  3727.                       { The row addresses must be decreased }
  3728.                       FormOp := opDelete;
  3729.                     FixFormulaRow(CP, FormOp, 0, Abs(LongInt(DestPos.Row) -
  3730.                       LongInt(SrcPos.Row)), MaxCols, MaxRows);
  3731.                   end; {...if (Formulas and CopyRowLitBit) = 0 }
  3732.                 end; {...if (CP <> nil) and CP^.ShouldUpdate }
  3733.               end; {...with CellPtr^}
  3734.               Inc(DestPos.Col);
  3735.             end; {...while Good and (DestPos.Col <= DestBlock.Stop.Col) }
  3736.             Inc(DestPos.Row);
  3737.           end; {...while Good and (DestPos.Row <= DestBlock.Stop.Row) }
  3738.  
  3739.           if DestBlock.CellInBlock(SrcPos) and (SourceSpreadSheet = @Self) then
  3740.           { Discard the original cell, since a new copy of it was added in
  3741.             the same position }
  3742.             Dispose(CellPtr, Done)
  3743.           else if (Operation = opMove) and Good then
  3744.           { if the source cell was in the destination block, and it was
  3745.             a move operation, then delete the source cell }
  3746.             SourceSpreadSheet^.DeleteCell(SrcPos, Deleted);
  3747.         end; {...if CellPtr <> Empty }
  3748.       end {...if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) }
  3749.     else
  3750.       begin
  3751.         if not (SameCellPos(BlockToCopy^.Start, DestBlock.Start) and
  3752.            (SourceSpreadSheet = @Self)) then
  3753.         { Continue only after verifying that a block is not going to be
  3754.           copied into itself }
  3755.         begin
  3756.           SetChanged(ModifiedYes);
  3757.           if (BlockToCopy^.Start.Col < DestBlock.Start.Col) and
  3758.              (SourceSpreadSheet = @Self) then
  3759.             { if the possibility exists that the blocks may overlap in such
  3760.               a way that cells of the source block are overwritten by the
  3761.               cells in the destination block before they are copied, then
  3762.               copy the blocks backwards }
  3763.             begin
  3764.               ColChange := -1;
  3765.               SrcPos.Col := BlockToCopy^.Stop.Col;
  3766.               DestPos.Col := DestBlock.Stop.Col;
  3767.             end {...if (BlockToCopy^.Start.Col < DestBlock.Start.Col) }
  3768.           else
  3769.             begin
  3770.               ColChange := 1;
  3771.               SrcPos.Col := BlockToCopy^.Start.Col;
  3772.               DestPos.Col := DestBlock.Start.Col;
  3773.             end; {...if/else }
  3774.           if (BlockToCopy^.Start.Row < DestBlock.Start.Row) and
  3775.              (SourceSpreadSheet = @Self) then
  3776.             { if the possibility exists that the blocks may overlap in such
  3777.               a way that cells of the source block are overwritten by the
  3778.               cells in the destination block before they are copied, then
  3779.               copy the blocks backwards }
  3780.             begin
  3781.               RowChange := -1;
  3782.               SrcPos.Row := BlockToCopy^.Stop.Row;
  3783.               DestPos.Row := DestBlock.Stop.Row;
  3784.             end {...if (BlockToCopy^.Start.Row < DestBlock.Start.Row) }
  3785.           else
  3786.             begin
  3787.               RowChange := 1;
  3788.               SrcPos.Row := BlockToCopy^.Start.Row;
  3789.               DestPos.Row := DestBlock.Start.Row;
  3790.             end; {...if/else }
  3791.  
  3792.           { Assign values to the SrcStartCol and DestStartCol which indicate
  3793.             the column of the first cell that has to be copied everytime a
  3794.             new row is selected for copying }
  3795.           SrcStartCol := SrcPos.Col;
  3796.           DestStartCol := DestPos.Col;
  3797.  
  3798.           with BlockToCopy^ do
  3799.           begin
  3800.             while Good and ((SrcPos.Row <= Stop.Row) and
  3801.                (SrcPos.Row >= Start.Row)) and (DestPos.Row <= MaxRows) do
  3802.             begin
  3803.               SrcPos.Col := SrcStartCol;
  3804.               DestPos.Col := DestStartCol;
  3805.               while Good and ((SrcPos.Col <= Stop.Col) and
  3806.                 (SrcPos.Col >= Start.Col)) and (DestPos.Col <= MaxCols) do
  3807.               begin
  3808.                 CellPtr := SourceCellHash^.Search(SrcPos);
  3809.                 CellHash.Delete(DestPos, CP);
  3810.                 if CP <> nil then
  3811.                   Dispose(CP, Done);
  3812.                 if (CellPtr <> Empty) and (CellPtr <> nil) then
  3813.                 begin
  3814.                   with CellPtr^ do
  3815.                   begin
  3816.                     Good := AddCell(CellType, DestPos, HasError, CurrValue,
  3817.                       CopyString);
  3818.                     if Good then
  3819.                     begin
  3820.                       CellPtr := CellHash.Search(DestPos);
  3821.                       if CellPtr^.ShouldUpdate then
  3822.                       begin
  3823.                         if (Formulas and CopyColLitBit) = 0 then
  3824.                         begin
  3825.                           if DestPos.Col >= SrcPos.Col then
  3826.                             FormOp := opInsert
  3827.                           else
  3828.                             FormOp := opDelete;
  3829.                           FixFormulaCol(CellPtr,FormOp, 0,
  3830.                             Abs(LongInt(DestPos.Col) - LongInt(SrcPos.Col)),
  3831.                             MaxCols, MaxRows);
  3832.                         end; {...if (Fomulas and CopyColLitBit) = 0 }
  3833.                         if (Formulas and CopyRowLitBit) = 0 then
  3834.                         begin
  3835.                           if DestPos.Row >= SrcPos.Row then
  3836.                             FormOp := opInsert
  3837.                           else
  3838.                             FormOp := opDelete;
  3839.                           FixFormulaRow(CellPtr, FormOp, 0,
  3840.                             Abs(LongInt(DestPos.Row) - LongInt(SrcPos.Row)),
  3841.                             MaxCols, MaxRows);
  3842.                         end; {...if (Formulas and CopyRowLitBit) = 0 }
  3843.                       end; {...if CellPtr^.ShouldUpdate }
  3844.                     end; {...if Good }
  3845.                   end; {...with CellPtr^ }
  3846.                 end; {...if (CellPtr <> Empty) and (CellPtr <> nil) }
  3847.                 if (Operation = opMove) and Good then
  3848.                 begin
  3849.                   SourceCellHash^.Delete(SrcPos, CP);
  3850.                   if CP <> nil then
  3851.                     Dispose(CP, Done);
  3852.                 end; {...if (Operation = opMove) and Good }
  3853.                 Inc(DestPos.Col, ColChange);
  3854.                 Inc(SrcPos.Col, ColChange);
  3855.               end; {...while Good and ((SrcPos.Col <= Stop.Col) and ... }
  3856.               Inc(DestPos.Row, RowChange);
  3857.               Inc(SrcPos.Row, RowChange);
  3858.             end; {...while Good and ((SrcPos.Row <= Stop.Row) and ... }
  3859.           end; {...with BlockToCopy^ }
  3860.         end; {...if not SameCellPos(BlockToCopy^.Start, DestBlock.Start) ... }
  3861.       end; {...if/else }
  3862.   end; {...with ClipBoard }
  3863. end; {...TSpreadSheet.PasteBlock }
  3864.  
  3865.  
  3866. procedure TSpreadSheet.PasteCellBlock;
  3867. { Copies a block from the source location to the current position }
  3868. var
  3869.   Dialog : PDialog;
  3870.   Block : TBlock;
  3871. begin
  3872.   with ClipBoard do
  3873.   begin
  3874.     { if the clipboard is active, then continue with the paste operation }
  3875.     if Active then
  3876.     begin
  3877.       { Determine the destination block }
  3878.       if BlockOn then
  3879.         Block.Init(CurrBlock^.Start)
  3880.       else
  3881.         Block.Init(CurrPos);
  3882.       if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) then
  3883.         { if its only one cell that will be copied in a block of cells then
  3884.           the destination block will be the currently selected block (if
  3885.           there is no block selected, the destination block will be the
  3886.           current cell }
  3887.         begin
  3888.           if BlockOn then
  3889.             Block.Stop := CurrBlock^.Stop
  3890.         end {...if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) }
  3891.       else
  3892.         { if a block of cells will be copied, then the destination block will
  3893.           have the same dimensions as the original block of cells }
  3894.         begin
  3895.           Inc(Block.Stop.Col, BlockToCopy^.Stop.Col - BlockToCopy^.Start.Col);
  3896.           Inc(Block.Stop.Row, BlockToCopy^.Stop.Row - BlockToCopy^.Start.Row);
  3897.         end; {...if/else }
  3898.  
  3899.       { Verifies that no protected cells are being deleted or overwritten }
  3900.       if SheetProtected then
  3901.       begin
  3902.         { Verifies that there are no protected cells in the destination
  3903.           block that could be overwritten }
  3904.         if CellsProtected(Block) then
  3905.         begin
  3906.           MessageBox(GLStringList^.Get(sCellsProtectedMsg), nil,
  3907.             mfInformation + mfOKButton);
  3908.           Exit;
  3909.         end ; {...if CellsProtected(Block) }
  3910.       end; {...if SheetProtected }
  3911.  
  3912.       { Execute the dialog requesting instructions on whether to update or
  3913.         not the formulas (if any) in the block to be copied or moved }
  3914.       Dialog := PDialog(GLResFile^.Get('FormulasDialog'));
  3915.       if Application^.ValidView(Dialog) <> nil then
  3916.       begin
  3917.         EraseMessage;
  3918.         if Desktop^.ExecView(Dialog) <> cmCancel then
  3919.         begin
  3920.           Dialog^.GetData(RCopyFormulas);
  3921.           MessageDialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  3922.           if Application^.ValidView(MessageDialog) <> nil then
  3923.             Desktop^.Insert(MessageDialog)
  3924.           else
  3925.             begin
  3926.               MessageDialog := nil;
  3927.               Exit;
  3928.             end; { else }
  3929.           PasteBlock(Block, RCopyFormulas.CopyFormulas);
  3930.           if MessageDialog <> nil then
  3931.           begin
  3932.             Desktop^.Delete(Dialog);
  3933.             Dispose(MessageDialog, Done);
  3934.             MessageDialog := nil;
  3935.           end; { if }
  3936.           if (SourceSpreadSheet <> @Self) and (SourceSpreadSheet <> nil) then
  3937.             SourceSpreadSheet^.DisplayAllCells;
  3938.           DisplayAllCells;
  3939.           ToggleClipBoardOff;
  3940.         end; {...if Desktop^.ExecView(Dialog) <> cmCancel }
  3941.         Dispose(Dialog, Done);
  3942.       end; {...if Application^.ValidView(Dialog) <> nil }
  3943.     end; {...if Active }
  3944.   end; {...with ClipBoard }
  3945. end; {...TSpreadSheet.PasteCellBlock }
  3946.  
  3947.  
  3948. procedure TSpreadSheet.Print;
  3949. { Prints the spreadsheet }
  3950. var
  3951.   Dialog : PDialog;
  3952.   Error,                   { Is set to true whenever an error ocurrs }
  3953.   Finished : Boolean;      { Is set to true when the print job is finished }
  3954.   FileString : PathStr;
  3955.   OutputFile : Text;       { File used for output }
  3956.   PageH,                   { Horizontal position of the page being printed }
  3957.   PageV,                   { Vertical position of the page being printed }
  3958.   SelectedCommand,         { Stores the result from the message box dialogs }
  3959.  
  3960.   StartCol,                { Starting column of the page being printed }
  3961.   StartRow : Word;         { Starting row of the page being printed }
  3962.  
  3963.   TopM, BottomM, LeftM,    {    Used to store the     }
  3964.   RightM, PageR, PageCols, {     values entered       }
  3965.   ColsN, ColsC : Byte;     {    in the Print Dialog   }
  3966.  
  3967.   Code : Integer;          { Return code of the val function }
  3968.  
  3969.  
  3970.     function CheckForEscape: Boolean;
  3971.     { Checks the event buffer to see if ESC has been pressed to
  3972.     cancel the print job }
  3973.     var
  3974.       Event : TEvent;
  3975.     begin
  3976.       CheckForEscape := False;
  3977.       GetEvent(Event);
  3978.       if Event.What = evKeyDown then
  3979.         if Event.KeyCode = kbEsc then
  3980.         begin
  3981.           { if ESC was pressed, delete the 'Printing...' dialog
  3982.             and prompt the user for confirmation }
  3983.           Desktop^.Delete(Dialog);
  3984.           if MessageBox(GLStringList^.Get(sCancelPrintJob), nil,
  3985.              mfError + mfYesButton + mfNoButton) = cmYes then
  3986.             CheckForEscape := True
  3987.           else
  3988.             Desktop^.Insert(Dialog);
  3989.         end {...if Event.KeyCode = kbEsc }
  3990.     end; {...CheckForEscape }
  3991.  
  3992.     function PrintChar(C: String): Boolean;
  3993.     { Prints a code to the assigned device without a sending a CR }
  3994.     begin
  3995.       PrintChar := True;
  3996.       repeat
  3997.         if CheckForEscape then
  3998.         begin
  3999.           PrintChar := False;
  4000.           Exit;
  4001.         end; {...if CheckForEscape }
  4002.         Error := False;
  4003.         {$I-}
  4004.         Write(OutputFile, C);
  4005.         {$I+}
  4006.         if IOResult <> 0 then
  4007.         begin
  4008.           Error := True;
  4009.           if FileString = DefaultPrinterName then
  4010.             begin
  4011.               Desktop^.Delete(Dialog);
  4012.               SelectedCommand := MessageBox(
  4013.                 GLStringList^.Get(sPrinterPrintErrorMsg), nil, mfError +
  4014.                 mfYesButton + mfNoButton);
  4015.               if SelectedCommand = cmNo then
  4016.                 PrintChar := False
  4017.               else
  4018.                 { Since the print job will continue, display again
  4019.                   the 'Printing...' dialog }
  4020.                 Desktop^.Insert(Dialog);
  4021.             end {...if FileString = DefaultPrinterName }
  4022.           else
  4023.             begin
  4024.               SelectedCommand := MessageBox(
  4025.                 GLStringList^.Get(sFilePrintErrorMsg), nil, mfError +
  4026.                 mfYesButton + mfNoButton);
  4027.               if SelectedCommand = cmNo then
  4028.                 PrintChar := False
  4029.               else
  4030.                 Desktop^.Insert(Dialog);
  4031.             end; {...if/else }
  4032.         end; {...if IOResult <> 0 }
  4033.       until not Error or (SelectedCommand = cmNo);
  4034.     end; {...PrintChar }
  4035.  
  4036.     function PrintString(S: String): Boolean;
  4037.     { Prints a string to the assigned device }
  4038.     begin
  4039.       PrintString := True;
  4040.       repeat
  4041.         if CheckForEscape then
  4042.         begin
  4043.           PrintString := False;
  4044.           Exit;
  4045.         end; {...if CheckForEscape }
  4046.         Error := False;
  4047.         {$I-}
  4048.         Writeln(OutputFile, S);
  4049.         {$I+}
  4050.         if IOResult <> 0 then
  4051.         begin
  4052.           Error := True;
  4053.           if FileString = DefaultPrinterName then
  4054.             begin
  4055.               SelectedCommand := MessageBox(
  4056.                 GLStringList^.Get(sPrinterPrintErrorMsg), nil, mfError +
  4057.                 mfYesButton + mfNoButton);
  4058.               if SelectedCommand = cmNo then
  4059.                 PrintString := False
  4060.               else
  4061.                 Desktop^.Insert(Dialog);
  4062.             end {...if FileString = DefaultPrinterName }
  4063.           else
  4064.             begin
  4065.               SelectedCommand := MessageBox(
  4066.                 GLStringList^.Get(sFilePrintErrorMsg), nil, mfError +
  4067.                 mfYesButton + mfNoButton);
  4068.               if SelectedCommand = cmNo then
  4069.                 PrintString := False
  4070.               else
  4071.                 Desktop^.Insert(Dialog);
  4072.             end; {...if/else }
  4073.         end; {...if IOResult <> 0}
  4074.       until not Error or (SelectedCommand = cmNo);
  4075.     end; {...PrintString }
  4076.  
  4077.     function RowStartString(Row: Word): String;
  4078.     { Returns the row string to be printed at the beginning of a line }
  4079.     begin
  4080.       RowStartString := '';
  4081.       with RPrint do
  4082.       begin
  4083.         if PrintRows <> 0 then
  4084.         begin
  4085.           if ((PrintRows = 1) and (PageH = 1)) or (PrintRows = 2) then
  4086.           begin
  4087.             RowStartString := LeftJustStr(RowToString(Row), RowNumberSpace);
  4088.             RowStartString[RowNumberSpace] := '│';
  4089.           end; {...if ((PrintRows = 1) and (PageH = 1)) or... }
  4090.         end; {...if PrintRows <> 0 }
  4091.       end; {...with RPrint }
  4092.     end; {...RowStartString }
  4093.  
  4094.     function PrintPage: Boolean;
  4095.     { Prints one page of the spreadsheet }
  4096.     var
  4097.       Color : Byte; { Simply used to fill the list of parameters for
  4098.                      the CellToFString method }
  4099.       Cols, Counter, Rows : Byte;
  4100.       Pos : CellPos;
  4101.       S : String;
  4102.     const
  4103.       OutlineBit = $01;
  4104.       BoldBit = $02;
  4105.     begin
  4106.       PrintPage := False;
  4107.       with RPrint, PrinterConfigRec do
  4108.       begin
  4109.  
  4110.         { Top margin }
  4111.         for Counter := 1 to TopM do
  4112.           if not PrintString('') then
  4113.             Exit;
  4114.  
  4115.         { Determine the number of rows that will fit in the page }
  4116.         Rows := Min((PageR - TopM - BottomM), Succ(MaxRows - StartRow));
  4117.  
  4118.         { One row will be used if the column headers will be printed }
  4119.         if PrintColumns in [1,2] then
  4120.           Dec(Rows);
  4121.  
  4122.         { Determine the number of columns that can fit in a page }
  4123.         Cols := 0;
  4124.         Counter := Length(RowStartString(StartRow));
  4125.         while Counter <= PageCols do
  4126.         begin
  4127.           Inc(Counter, ColWidth(Cols + StartCol));
  4128.           Inc(Cols);
  4129.         end; {...while Counter <= PageCols }
  4130.         Dec(Cols);
  4131.         Cols := Min(Cols, Succ(MaxCols - StartCol));
  4132.  
  4133.         if ((PrintColumns = 1) and (PageV = 1)) or (PrintColumns = 2) then
  4134.         { Print the column headers if requested }
  4135.         begin
  4136.           S := FillString(Length(RowStartString(StartRow)), ' ');
  4137.           for Counter := StartCol to Pred(StartCol + Cols) do
  4138.             S := S + CenterStr(ColumnToString(Counter), ColWidth(Counter));
  4139.           if not PrintChar(PrinterUnderlineOnCode) then
  4140.             Exit;
  4141.           if (Other and BoldBit) <> 0 then
  4142.             if not PrintChar(PrinterBoldOnCode) then
  4143.               Exit;
  4144.           if not PrintString(S) then
  4145.             Exit;
  4146.           if (Other and BoldBit) <> 0 then
  4147.             if not PrintChar(PrinterBoldOffCode) then
  4148.               Exit;
  4149.           if not PrintChar(PrinterUnderlineOffCode) then
  4150.             Exit;
  4151.         end; {...if ((PrintColumns = 1) and (PageV = 1))... }
  4152.  
  4153.         { Print the data }
  4154.         for Pos.Row := StartRow to Pred(StartRow + Rows) do
  4155.         begin
  4156.           S := RowStartString(Pos.Row);
  4157.           if S <> '' then
  4158.           { Print the row numbers }
  4159.           begin
  4160.             if (Other and BoldBit) <> 0 then
  4161.               if not PrintChar(PrinterBoldOnCode) then
  4162.                 Exit;
  4163.             if not PrintChar(S) then
  4164.               Exit;
  4165.             if (Other and BoldBit) <> 0 then
  4166.               if not PrintChar(PrinterBoldOffCode) then
  4167.                 Exit;
  4168.             S := '';
  4169.           end; {...if S <> '' }
  4170.           for Pos.Col := StartCol to Pred(StartCol + Cols) do
  4171.             S := S + CellToFString(Pos, Color);
  4172.           if not PrintString(S) then
  4173.             Exit;
  4174.         end; {...for Pos.Row }
  4175.  
  4176.         Inc(StartCol, Cols);
  4177.         if (StartCol > LastPos.Col) or (StartCol = 0) then
  4178.           begin
  4179.             Inc(StartRow, Rows);
  4180.             if (StartRow > LastPos.Row) or (StartRow = 0) then
  4181.               Finished := True
  4182.             else
  4183.               begin
  4184.                 Inc(PageV);
  4185.                 PageH := 1;
  4186.                 StartCol := 1;
  4187.               end; {...if/else }
  4188.           end {...if (StartCol > LastPos.Col) or (StartCol = 0) }
  4189.         else
  4190.           Inc(PageH);
  4191.         if not PrintChar(Chr(FF)) then
  4192.           Exit;
  4193.       end; {...with RPrint, PrinterConfigRec }
  4194.       PrintPage := True;
  4195.     end; {...PrintPage }
  4196.  
  4197.     procedure EndPrintJob;
  4198.     { Does all the necessary clean up when finishing a print job }
  4199.     begin
  4200.       Close(OutputFile);
  4201.       InitSysError;
  4202.     end; {...EndPrintJob }
  4203.  
  4204. begin
  4205.   Dialog := PDialog(GLResFile^.Get('PrintDialog'));
  4206.   Dialog^.SetData(RPrint);
  4207.   if Application^.ValidView(Dialog) <> nil then
  4208.     begin
  4209.       if Desktop^.ExecView(Dialog) <> cmCancel then
  4210.         Dialog^.GetData(RPrint)
  4211.       else
  4212.         begin
  4213.           Dispose(Dialog, Done);
  4214.           Exit;
  4215.         end; {...if/else }
  4216.     end {...if Application^.ValidView(Dialog) <> nil }
  4217.   else
  4218.     Exit;
  4219.   Dispose(Dialog, Done);
  4220.   with RPrint, PrinterConfigRec do
  4221.   begin
  4222.     if PrintTo = 0 then
  4223.       FileString := DefaultPrinterName
  4224.     else
  4225.       begin
  4226.         Dialog := PFileDialog(GLResFile^.Get('PrintToDialog'));
  4227.         if Application^.ValidView(Dialog) <> nil then
  4228.           begin
  4229.             if Desktop^.ExecView(Dialog) <> cmCancel then
  4230.               Dialog^.GetData(FileString)
  4231.             else
  4232.               begin
  4233.                 Dispose(Dialog, Done);
  4234.                 Exit;
  4235.               end; {...if/else }
  4236.           end {...if Application^.ValidView(Dialog) <> nil }
  4237.         else
  4238.           Exit;
  4239.         Dispose(Dialog, Done);
  4240.       end; {...if/else }
  4241.  
  4242.     { Disables Turbo Vision's system error handler to be able to handle
  4243.       print errors differently }
  4244.     DoneSysError;
  4245.  
  4246.     repeat
  4247.       Error := False;
  4248.       {$I-}
  4249.       Assign(OutputFile, FileString);
  4250.       Rewrite(OutputFile);
  4251.       {$I+}
  4252.       if IOResult <> 0 then
  4253.       { if the file could not be opened, prompt the user wether to
  4254.         continue with or cancel the print job }
  4255.       begin
  4256.         Error := True;
  4257.         SelectedCommand := MessageBox(GLStringList^.Get(sPrintInitErrorMsg),
  4258.           nil, mfYesButton + mfNoButton);
  4259.         if SelectedCommand = cmNo then
  4260.         begin
  4261.           EndPrintJob;
  4262.           Exit;
  4263.         end; {...if SelectedCommand = cmNo }
  4264.       end; {...if IOResult <> 0 }
  4265.     until not Error;
  4266.  
  4267.     { Convert to numbers the values entered in the 'Print Dialog' }
  4268.     val(TopMargin, TopM, Code);
  4269.     val(BottomMargin, BottomM, Code);
  4270.     val(LeftMargin, LeftM, Code);
  4271.     val(RightMargin, RightM, Code);
  4272.     val(PageRows, PageR, Code);
  4273.     val(NormalCols, ColsN, Code);
  4274.     val(CondensedCols, ColsC, Code);
  4275.  
  4276.     { Determine the number of columns available for printing }
  4277.     if PrintSize = 1 then
  4278.       begin
  4279.         if not PrintChar(PrinterCondensedOnCode) then
  4280.         begin
  4281.           EndPrintJob;
  4282.           Exit;
  4283.         end; {...if not PrintChar(PrinterCondensedOnCode) }
  4284.         PageCols := ColsC;
  4285.       end {...if PrintSize = 1}
  4286.     else
  4287.       PageCols := ColsN;
  4288.     PageV := 1;
  4289.     PageH := 1;
  4290.     StartCol := 1;
  4291.     StartRow := 1;
  4292.     Finished := False;
  4293.  
  4294.     { Display a dialog to indicate the file is being printed }
  4295.     Dialog := PDialog(GLResFile^.Get('PrintingDialog'));
  4296.     if Application^.ValidView(Dialog) <> nil then
  4297.       Desktop^.Insert(Dialog)
  4298.     else
  4299.       begin
  4300.         if Dialog <> nil then
  4301.           Dispose(Dialog, Done);
  4302.         EndPrintJob;
  4303.         Exit;
  4304.       end; {...if/else }
  4305.     repeat
  4306.       if not PrintPage then
  4307.       begin
  4308.         EndPrintJob;
  4309.         { It is not necessary to delete the dialog from the desktop
  4310.           since the dialog is deleted before prompting the user
  4311.           for cancelation }
  4312.         Dispose(Dialog, Done);
  4313.         Exit;
  4314.       end; {...if not PrintPage }
  4315.     until Finished;
  4316.     if not PrintChar(PrinterCondensedOffCode) or
  4317.        not PrintChar(PrinterUnderlineOffCode) then
  4318.     begin
  4319.       EndPrintJob;
  4320.       Desktop^.Delete(Dialog);
  4321.       Dispose(Dialog, Done);
  4322.       Exit;
  4323.     end; {...if not PrintChar(PrinterCondensedOffCode) or ... }
  4324.     EndPrintJob;
  4325.     Desktop^.Delete(Dialog);
  4326.     Dispose(Dialog, Done);
  4327.   end; {...with RPrint, PrinterConfigRec }
  4328. end; {...TSpreadSheet.Print }
  4329.  
  4330.  
  4331. procedure TSpreadSheet.Recalc(Display: Boolean);
  4332. { Recalculates all the values that need to be recalculated }
  4333. var
  4334.   Pos : CellPos;
  4335.  
  4336.     procedure DoUpdate;
  4337.     var
  4338.       NewPos : CellPos;
  4339.       CellPtr : PCell;
  4340.       CellsOverWritten : Word;
  4341.       FormulaStr : PString;
  4342.     begin
  4343.       with CellHash do
  4344.       begin
  4345.         CellPtr := Search(Pos);
  4346.         if CellPtr^.ShouldUpdate then
  4347.         begin
  4348.           with PFormulaCell(CellPtr)^ do
  4349.           begin
  4350.             FormulaStr := NewStr(Formula.ToString);
  4351.             Parser^.Init(@CellHash, FormulaStr, MaxCols, MaxRows);
  4352.             Parser^.Parse;
  4353.             DisposeStr(FormulaStr);
  4354.             Value := Parser^.ParseValue;
  4355.             Error := Parser^.ParseError;
  4356.             SetChanged(ModifiedYes);
  4357.             CellsOverWritten := CellPtr^.OverWritten(CellHash, FormatHash,
  4358.               WidthHash, LastPos, MaxCols, GetColWidth, DisplayFormulas);
  4359.             if OverWriteHash.Change(CellPtr, CellsOverWritten) and Display and
  4360.                (CellPtr^.Loc.Col + CellsOverWritten >=
  4361.                ScreenBlock^.Start.Col) then
  4362.             begin
  4363.               NewPos := CellPtr^.Loc;
  4364.               for NewPos.Col := CellPtr^.Loc.Col to ScreenBlock^.Stop.Col do
  4365.               begin
  4366.                 if ScreenBlock^.CellInBlock(NewPos) then
  4367.                   DisplayCell(NewPos);
  4368.               end; {...for NewPos.Col }
  4369.             end; {...if OverWriteHash.Change(CellPtr, CellsOverWritten) ... }
  4370.           end; {...with PFormulaCell(CellPtr)^ }
  4371.         end; {...if CellPtr^.ShouldUpdate }
  4372.       end; {...with CellHash }
  4373.     end; {...DoUpdate }
  4374.  
  4375. begin
  4376.   DisplayMessage(GLStringList^.Get(sRecalcMsg));
  4377.   for Pos.Row := 1 to LastPos.Row do
  4378.     for Pos.Col := 1 to LastPos.Col do
  4379.       DoUpdate;
  4380.   for Pos.Row := LastPos.Row downto 1 do
  4381.     for Pos.Col := LastPos.Col downto 1 do
  4382.       DoUpdate;
  4383.   EraseMessage;
  4384. end; {...TSpreadSheet.Recalc }
  4385.  
  4386.  
  4387. function TSpreadsheet.RowToY(Row : Integer) : Byte;
  4388. { Returns the screen position of a particular row }
  4389. begin
  4390.   RowToY := (Row - ScreenBlock^.Start.Row) + DisplayArea.UpperLeft.Row ;
  4391. end; {...TSpreadSheet.RowToY }
  4392.  
  4393.  
  4394. function TSpreadSheet.SameCellPos(P1, P2 : CellPos) : Boolean;
  4395. { Returns true if two positions are the same }
  4396. begin
  4397.   SameCellPos := Compare(P1, P2, SizeOf(CellPos));
  4398. end; {...TSpreadSheet.SameCellPos }
  4399.  
  4400.  
  4401. {****************************************************************************}
  4402. { TSpreadSheet.SetFormat                                                     }
  4403. {****************************************************************************}
  4404. procedure TSpreadSheet.SetFormat(Block: TBlock; DecimalPlaces: Byte;
  4405.   Justification, NumberFormat: Word; CurrencyChar: Char);
  4406. var
  4407.   Format: FormatType;
  4408. begin
  4409.   Format := DecimalPlaces + (Justification shl JustShift) + (NumberFormat
  4410.     shl NumberFormatShift) + (Ord(CurrencyChar) shl CurrencyShift);
  4411.   if not FormatHash.Add(Block.Start, Block.Stop, Format) then
  4412.     Exit;
  4413.   FixBlockOverwrite(Block);
  4414. end;
  4415.  
  4416.  
  4417. function TSpreadSheet.SelectColumn(var Event: TEvent): Boolean;
  4418. { Marks a complete column as selected }
  4419. var
  4420.   Pos : CellPos;
  4421.   SelectedCol : Integer;
  4422.   Block : TBlock;
  4423.   Mouse : TPoint;
  4424. begin
  4425.    MakeLocal(Event.Where, Mouse);
  4426.    if ColArea.PointInArea(Mouse.X, Mouse.Y) then
  4427.      begin
  4428.        ClearCurrBlock;
  4429.        SelectedCol := XToCol(Mouse.X);
  4430.        if SelectedCol = 0 then
  4431.          Exit;
  4432.        Pos := CurrPos;
  4433.        CurrPos.Row := 1;
  4434.        CurrPos.Col := SelectedCol;
  4435.        ToggleBlockOn;
  4436.        CurrPos.Row := ScreenBlock^.Start.Row;
  4437.        if ScreenBlock^.CellInBlock(Pos) then
  4438.          MoveCell(Pos);
  4439.        Pos.Row := MaxRows;
  4440.        Pos.Col := SelectedCol;
  4441.        CurrBlock^.Stop := Pos;
  4442.        Block.Start := CurrBlock^.Start;
  4443.        Pos.Row := ScreenBlock^.Stop.Row;
  4444.        Block.Stop := Pos;
  4445.        DisplayBlock(Block);
  4446.        DisplayCellData;
  4447.        ClearEvent(Event);
  4448.        SelectColumn := True;
  4449.      end {...if ColArea.PointInArea(Mouse.X, Mouse.Y) }
  4450.    else
  4451.      SelectColumn := False;
  4452. end; {...TSpreadSheet.SelectColumn }
  4453.  
  4454.  
  4455. procedure TSpreadSheet.ScrollDraw;
  4456. { Redraws the spreadsheet whenever the scrollbar changes }
  4457. var
  4458.   Redraw : Boolean;
  4459.   D : TPoint;
  4460. begin
  4461.   Desktop^.Lock;
  4462.   if HScrollBar <> nil then
  4463.     D.X := HScrollBar^.Value
  4464.   else
  4465.     D.X := 0;
  4466.   if VScrollBar <> nil then
  4467.     D.Y := VScrollBar^.Value
  4468.   else
  4469.     D.Y := 0;
  4470.   if D.X <> Delta.X then
  4471.   begin
  4472.     with PlimScrollBar(HScrollBar)^, ScreenBlock^ do
  4473.     begin
  4474.       if (Abs(Change) = 1) and not KeyPressed then
  4475.         begin
  4476.           if Abs(Change) = Change then
  4477.             begin
  4478.               if Stop.Col < MaxCols then
  4479.                 begin
  4480.                   Inc(Stop.Col);
  4481.                   SetScreenColStop(Stop.Col);
  4482.                   Redraw := True;
  4483.                 end {...if Stop.Col < MaxCols }
  4484.               else
  4485.                 SetValue(Delta.X);
  4486.             end {...if Abs(Change) = Change }
  4487.           else
  4488.             begin
  4489.               if Start.Col > 1 then
  4490.                 begin
  4491.                   Dec(Start.Col);
  4492.                   SetScreenColStart(Start.Col);
  4493.                   Redraw := True;
  4494.                 end {...if Start.Col > 1 }
  4495.               else
  4496.                 SetValue(Delta.X);
  4497.             end; {...if/else }
  4498.           if Redraw then
  4499.           begin
  4500.             SetBlankArea;
  4501.             DisplayCols;
  4502.             DisplayAllCells;
  4503.             DisplayCellData;
  4504.             if Value <> Start.Col then
  4505.             begin
  4506.               Value := Start.Col;
  4507.               HScrollBar^.DrawView;
  4508.             end; {...if Value <> Start.Col }
  4509.             Delta.X := Value;
  4510.           end; {...if Redraw }
  4511.         end {...if (Abs(Change) = 1) and not KeyPressed }
  4512.       else if (Abs(Change) = PgStep) and not KeyPressed then
  4513.         begin
  4514.           if Abs(Change) = Change then
  4515.             begin
  4516.               if Stop.Col < MaxCols then
  4517.                 begin
  4518.                   Start.Col := Succ(Stop.Col);
  4519.                   SetScreenColStart(Start.Col);
  4520.                   Redraw := True;
  4521.                 end {...if Stop.Col < MaxCols }
  4522.               else
  4523.                 SetValue(Delta.X);
  4524.             end {...if Abs(Change) = Change }
  4525.           else
  4526.             begin
  4527.               if Start.Col > 1 then
  4528.                 begin
  4529.                   Stop.Col := Pred(Start.Col);
  4530.                   SetScreenColStop(Stop.Col);
  4531.                   Redraw := True;
  4532.                 end {...if Start.Col > 1 }
  4533.               else
  4534.                 SetValue(Delta.X);
  4535.             end; {...if/else }
  4536.           if Redraw then
  4537.           begin
  4538.             SetBlankArea;
  4539.             DisplayCols;
  4540.             DisplayAllCells;
  4541.             DisplayCellData;
  4542.             if Value <> Start.Col then
  4543.               begin
  4544.                 Value := Start.Col;
  4545.                 HScrollBar^.DrawView;
  4546.               end; {...if Value <> Start.Col }
  4547.             Delta.X := Value;
  4548.           end; {...if Redraw }
  4549.         end {...else if (Abs(Change) = PgStep) and not KeyPressed }
  4550.       else
  4551.         begin
  4552.           if (Value <= MaxCols) and (Value >= 1) then
  4553.             begin
  4554.               Start.Col := Value;
  4555.               if KeyPressed then
  4556.                 ExtendCurrBlock(RedrawNo);
  4557.               SetScreenColStart(Start.Col);
  4558.               SetBlankArea;
  4559.               DisplayCols;
  4560.               DisplayAllCells;
  4561.               DisplayCellData;
  4562.               Delta.X := Value;
  4563.             end {...if (Value <= MaxCols) and (Value >= 1) }
  4564.           else
  4565.             SetValue(Delta.X);
  4566.         end; {...if/else }
  4567.     end; {...with PLimScrollBar(HScrollBar^), ScreenBlock^ }
  4568.   end; {...if D.X <> Delta.X }
  4569.   if D.Y <> Delta.Y then
  4570.   begin
  4571.     with PLimScrollBar(VScrollBar)^, ScreenBlock^ do
  4572.     begin
  4573.       if (Abs(Change) = 1) and not KeyPressed then
  4574.         begin
  4575.           if Abs(Change) = Change then
  4576.             begin
  4577.               if Stop.Row < MaxRows then
  4578.                 begin
  4579.                   Inc(Stop.Row);
  4580.                   SetScreenRowStop(Stop.Row);
  4581.                   Redraw := True;
  4582.                 end {...if Stop.Row < MaxRows }
  4583.               else
  4584.                 SetValue(Delta.Y);
  4585.             end {...if Abs(Change) = Change }
  4586.           else
  4587.             begin
  4588.               if Start.Row > 1 then
  4589.                 begin
  4590.                   Dec(Start.Row);
  4591.                   SetScreenRowStart(Start.Row);
  4592.                   Redraw := True;
  4593.                 end {...if Start.Row > 1 }
  4594.               else
  4595.                 SetValue(Delta.Y);
  4596.             end; {...if/else }
  4597.           if Redraw then
  4598.           begin
  4599.             DisplayRows;
  4600.             DisplayAllCells;
  4601.             DisplayCellData;
  4602.             if Value <> Start.Row then
  4603.             begin
  4604.               Value := Start.Row;
  4605.               VScrollBar^.DrawView;
  4606.             end; {...if Value <> Start.Row }
  4607.             Delta.Y := Value;
  4608.           end; {...if Redraw }
  4609.         end {...if (Abs(Change) = 1) and not KeyPressed }
  4610.       else if (Abs(Change) = PgStep) and not KeyPressed then
  4611.         begin
  4612.           if Abs(Change) = Change then
  4613.             begin
  4614.               if Stop.Row < MaxRows then
  4615.                 begin
  4616.                   Start.Row := Start.Row + TotalRows;
  4617.                   if Start.Row > MaxRows then
  4618.                     Start.Row := MaxRows;
  4619.                   SetScreenRowStart(Start.Row);
  4620.                   Redraw := True;
  4621.                 end {...if Stop.Row < MaxRows }
  4622.               else
  4623.                 SetValue(Delta.Y);
  4624.            end {...if Abs(Change) = Change }
  4625.          else
  4626.            begin
  4627.              if Start.Row > 1 then
  4628.                begin
  4629.                  Start.Row := Start.Row - TotalRows;
  4630.                  if Start.Row < 1 then
  4631.                    Start.Row := 1;
  4632.                  SetScreenRowStart(Start.Row);
  4633.                  Redraw := True;
  4634.                end {...if Start.Row > 1 }
  4635.              else
  4636.                SetValue(Delta.Y);
  4637.            end; {...if/else }
  4638.          if Redraw then
  4639.          begin
  4640.            DisplayRows;
  4641.            DisplayAllCells;
  4642.            DisplayCellData;
  4643.            if Value <> Start.Row then
  4644.            begin
  4645.              Value := Start.Row;
  4646.              VScrollBar^.DrawView;
  4647.            end; {...if Value <> Start.Row }
  4648.            Delta.Y := Value;
  4649.          end; {...if Redraw }
  4650.        end {...else if (Abs(Change) = PgStep) and not KeyPressed }
  4651.      else
  4652.        begin
  4653.          if (Value <= MaxRows) and (Value >= 1) then
  4654.            begin
  4655.              Start.Row := Value;
  4656.              if KeyPressed then
  4657.                ExtendCurrBlock(RedrawNo);
  4658.              SetScreenRowStart(Start.Row);
  4659.              DisplayRows;
  4660.              DisplayAllCells;
  4661.              DisplayCellData;
  4662.              Delta.Y := Value;
  4663.            end {...if (Value <= MaxRows) and (Value >= 1) }
  4664.          else
  4665.            SetValue(Delta.Y);
  4666.        end; {...if/else }
  4667.     end; {...with PLimScrollBar(VScrollBar)^, ScreenBlock^ }
  4668.   end; {...if D.Y <> Delta.Y }
  4669.   Desktop^.Unlock;
  4670. end; {...TSpreadSheet.ScrollDraw }
  4671.  
  4672.  
  4673. procedure TSpreadSheet.SetAreas(ScrollArea:TRect);
  4674. { Sets the locations of the different areas of the spreadsheet }
  4675. var
  4676.   x1, x2, y1, y2 : Byte;
  4677. begin
  4678.   x1 := ScrollArea.A.X;
  4679.   y1 := ScrollArea.A.Y;
  4680.   x2 := Pred(ScrollArea.B.X);
  4681.   y2 := Pred(ScrollArea.B.Y);
  4682.   TotalRows := Pred(y2 - Succ(y1));
  4683.   ColArea.Init(x1 + RowNumberSpace, y1, x2, y1, GetColor(6));
  4684.   RowArea.Init(x1, Succ(Y1), Pred(x1 + RowNumberSpace), Pred(Pred(y2)),
  4685.     GetColor(7));
  4686.   InfoArea.Init(x1, y1, Pred(x1 + RowNumberSpace), y1, GetColor(10));
  4687.   DisplayArea.Init(x1 + RowNumberSpace, Succ(y1), x2, Pred(Pred(y2)),
  4688.     GetColor(1));
  4689.   DataArea.Init (x1, Pred(y2), x2, Pred(y2), GetColor(1));
  4690.   ContentsArea.Init (x1, y2, x2, y2, GetColor(9));
  4691.   SetScreenColStart(ScreenBlock^.Start.Col);
  4692.   SetScreenRowStart(ScreenBlock^.Start.Row);
  4693.   SetBlankArea;
  4694. end; {...TSpreadSheet.SetAreas }
  4695.  
  4696. {****************************************************************************}
  4697. { TSpreadSheet.SetAvailableCommands                                          }
  4698. {****************************************************************************}
  4699. procedure TSpreadSheet.SetAvailableCommands;
  4700. { Enables all commands handled by TSpreadSheet.  The commands enabled will
  4701.   depend on whether the spreadsheet is protected or not. }
  4702. begin
  4703.   if not SheetProtected then
  4704.     EnableCommands([cmRecalc, cmToggleAutoCalc, cmToggleFormulas, cmEditCell,
  4705.       cmGoToCell, cmChangeColWidth, cmDeleteColumns, cmInsertColumns,
  4706.       cmDeleteRows, cmInsertRows, cmFormatCells, cmFormatDefault, cmClear,
  4707.       cmCopy, cmPaste, cmCut, cmChangeColHeaders, cmDeleteColHeaders,
  4708.       cmToggleHeaders, cmToggleProtection, cmSetUnlocked, cmSetLocked,
  4709.       cmSortData, cmPrintSheet])
  4710.   else
  4711.     begin
  4712.       EnableCommands([cmRecalc, cmToggleAutoCalc, cmEditCell,
  4713.         cmGoToCell, cmClear, cmCopy, cmPaste, cmCut, cmToggleProtection,
  4714.         cmPrintSheet]);
  4715.       DisableCommands([cmChangeColHeaders, cmDeleteColHeaders,
  4716.         cmToggleHeaders, cmToggleFormulas, cmChangeColWidth, cmDeleteColumns,
  4717.         cmInsertColumns, cmDeleteRows, cmInsertRows, cmFormatCells,
  4718.         cmFormatDefault, cmSetUnlocked, cmSetLocked, cmSortData])
  4719.     end;
  4720. end;
  4721.  
  4722. procedure TSpreadSheet.SetBlankArea;
  4723. { Determines if there is a blank area and its location }
  4724. var
  4725.   C : Integer;
  4726. begin
  4727.   Move(DisplayArea, BlankArea, SizeOf(DisplayArea));
  4728.   with BlankArea do
  4729.   begin
  4730.     with ScreenBlock^ do
  4731.       C := ColStart^[Stop.Col - Start.Col] + ColWidth(Stop.Col);
  4732.     if C > DisplayArea.LowerRight.Col then
  4733.       NoBlankArea := True
  4734.     else
  4735.       begin
  4736.         NoBlankArea := False;
  4737.         UpperLeft.Col := C;
  4738.       end; {...if/else }
  4739.   end; {...with BlankArea }
  4740. end; {...TSpreadSheet.SetBlankArea }
  4741.  
  4742.  
  4743.  
  4744. procedure TSpreadSheet.SetChanged(IsChanged: Boolean);
  4745. { Changes the Modified state of the spreadsheet }
  4746. begin
  4747.   Modified := IsChanged;
  4748.   if DisplayEnabled then
  4749.     DisplayInfo;
  4750. end; {...TSpreadSheet.SetChanged }
  4751.  
  4752.  
  4753. procedure TSpreadSheet.SetLimit(X, Y: Integer);
  4754. { Sets the limits of the spreadsheet and adjusts the scrollbars accordingly }
  4755. var
  4756.   R : TRect;
  4757. begin
  4758.   Limit.X := X;
  4759.   Limit.Y := Y;
  4760.   if HScrollBar <> nil then
  4761.     with HScrollBar^ do
  4762.       SetParams (Value, 1, X, Succ(ScreenBlock^.Stop.Col -
  4763.         ScreenBlock^.Start.Col), 1);
  4764.   if VScrollBar <> nil then
  4765.     with VScrollBar^ do
  4766.       SetParams (Value, 1, Y, TotalRows, 1);
  4767. end; {...TSpreadSheet.SetLimit }
  4768.  
  4769.  
  4770. procedure TSpreadSheet.SetLocked;
  4771. { Restores the cells to the locked state, preventing the modification of the
  4772.   cells' contents when the sheet is protected }
  4773. begin
  4774.   if BlockOn then
  4775.     UnlockedHash.Delete(CurrBlock^.Start, CurrBlock^.Stop)
  4776.   else
  4777.     UnlockedHash.Delete(CurrPos, CurrPos);
  4778.   DisplayCellData;
  4779.   SetChanged(ModifiedYes);
  4780. end; {...TSpreadSheet.SetLocked }
  4781.  
  4782.  
  4783. procedure TSpreadSheet.SetNameWithMouse(var Event: TEvent);
  4784. { Checks to see if the mouse was DoubleClicked in the col area, and if so,
  4785.   it calls the ChangeColNames method }
  4786. var
  4787.   Mouse : TPoint;
  4788.   RealCurrPosCol : Word;
  4789.   SelectedCol : Word;
  4790. begin
  4791.   MakeLocal(Event.Where, Mouse);
  4792.   if ColArea.PointInArea(Mouse.X, Mouse.Y) then
  4793.   begin
  4794.     RealCurrPosCol := CurrPos.Col;
  4795.     SelectedCol := XToCol(Mouse.X);
  4796.     if SelectedCol = 0 then
  4797.       Exit
  4798.     else
  4799.       CurrPos.Col := SelectedCol;
  4800.     ChangeColHeaders;
  4801.     CurrPos.Col := RealCurrPosCol;
  4802.     ClearEvent(Event);
  4803.   end; {...if ColArea.PointInArea(Mouse.X, Mouse.Y) }
  4804. end; {...TSpreadSheet.SetNameWithMouse }
  4805.  
  4806.  
  4807. procedure TSpreadSheet.SetProtection(Enable, Display: Boolean);
  4808. { Protects or unprotects the sheet from unauthorized changes }
  4809. begin
  4810.   if Enable then
  4811.     SheetProtected := True
  4812.   else
  4813.     SheetProtected := False;
  4814.   SetAvailableCommands;
  4815.   if Display then
  4816.   begin
  4817.     DisplayAllCells;
  4818.     DisplayCellData;
  4819.   end; {...if Display }
  4820. end; {...TSpreadSheet.SetProtection }
  4821.  
  4822.  
  4823. procedure TSpreadSheet.SetScreenColStart(NewCol:Integer);
  4824. { Determines the starting and ending columns when the starting column is known }
  4825. begin
  4826.   ScreenBlock^.Start.Col := NewCol;
  4827.   FindScreenColStop;
  4828.   FindScreenColStart;
  4829. end; {...TSpreadSheet.SetScreenColStart }
  4830.  
  4831.  
  4832. procedure TSpreadSheet.SetScreenColStop(NewCol:Integer);
  4833. { Determines the starting and ending columns when the ending column is known }
  4834. begin
  4835.   ScreenBlock^.Stop.Col := NewCol;
  4836.   FindScreenColStart;
  4837.   FindScreenColStop;
  4838. end; {...TSpreadSheet.SetScreenColStop }
  4839.  
  4840.  
  4841. procedure TSpreadSheet.SetScreenRowStart(NewRow:Integer);
  4842. { Determines the starting and ending rows when the starting row is known }
  4843. begin
  4844.   ScreenBlock^.Start.Row := NewRow;
  4845.   FindScreenRowStop;
  4846. end; {...TSpreadSheet.SetScreenRowStart }
  4847.  
  4848.  
  4849. procedure TSpreadSheet.SetScreenRowStop(NewRow:Integer);
  4850. { Determines the starting and ending rows when the ending row is known }
  4851. begin
  4852.   ScreenBlock^.Stop.Row := NewRow;
  4853.   FindScreenRowStart;
  4854. end; {...TSpreadSheet.SetScreenRowStop }
  4855.  
  4856.  
  4857. procedure TSpreadSheet.SetState(AState: Word; Enable: Boolean);
  4858. { Changes the state of the spreadsheet and displays or hides the cursor
  4859.   depending on whether the spreadsheet is activated or deactivated }
  4860. begin
  4861.   if AState = sfActive then
  4862.   begin
  4863.     SetProtection(SheetProtected, False);
  4864.     if Enable then
  4865.       begin
  4866.         CurrPos := OldCurrPos;
  4867.         if ScreenBlock^.CellInBlock(CurrPos) or
  4868.            (CurrPos.Col = Succ(ScreenBlock^.Stop.Col)) then
  4869.           DisplayCell(CurrPos);
  4870.       end {...if Enable }
  4871.     else
  4872.       begin
  4873.         OldCurrPos := CurrPos;
  4874.         CurrPos.Col := Succ(ScreenBlock^.Stop.Col);
  4875.         CurrPos.Row := Succ(ScreenBlock^.Stop.Row);
  4876.         if ScreenBlock^.CellInBlock(OldCurrPos) or
  4877.            (OldCurrPos.Col = Succ(ScreenBlock^.Stop.Col)) then
  4878.           DisplayCell(OldCurrPos);
  4879.       end; {...if/else }
  4880.   end; {...if AState = sfActive }
  4881.   TScroller.SetState(AState, Enable);
  4882. end; {...TSpreadSheet.SetState }
  4883.  
  4884.  
  4885. procedure TSpreadSheet.SetUnlocked;
  4886. { Mark the cell or group of cells as unlocked, allowing the modification of
  4887.   the cells' contents even when the sheet is protected }
  4888. begin
  4889.   if BlockOn then
  4890.     UnlockedHash.Add(CurrBlock^.Start, CurrBlock^.Stop)
  4891.   else
  4892.     UnlockedHash.Add(CurrPos, CurrPos);
  4893.   DisplayCellData;
  4894.   SetChanged(ModifiedYes);
  4895. end; {...TSpreadSheet.SetUnlocked }
  4896.  
  4897.  
  4898. procedure TSpreadSheet.SortData;
  4899. { Sorts the data in the current block using up to three different keys }
  4900. var
  4901.   Dialog : PDialog;
  4902.   Block : TBlock;   { Block of data that will be sorted }
  4903.   Pos : CellPos;    { Used only to complete parameter list }
  4904.   F : File;
  4905.  
  4906.     function SortOrder(CheckBoxItem: Byte): SortTypes;
  4907.     { Returns the sort type value corresponding to the checkbox item selected }
  4908.     begin
  4909.       if CheckBoxItem = 0 then
  4910.         SortOrder := Ascending
  4911.       else
  4912.         SortOrder := Descending;
  4913.     end; {...SortOrder }
  4914.  
  4915.     function KeyColumn(KeyValue: String): Word;
  4916.     { Returns the corresponding column for the given string }
  4917.     var
  4918.       IndicatorLength: Byte;
  4919.       Pos : CellPos;
  4920.       Indicator : String;
  4921.       Col, FormLen : Word;
  4922.     begin
  4923.       Col := 0;
  4924.       IndicatorLength := Length(GLStringList^.Get(sColumnEntryIndicator)+' ');
  4925.       Indicator := Copy(KeyValue, 1, IndicatorLength);
  4926.       if Indicator = (GLStringList^.Get(sColumnEntryIndicator)+' ') then
  4927.       begin
  4928.         Indicator := Copy(KeyValue, Succ(IndicatorLength), (Length(KeyValue) -
  4929.           IndicatorLength));
  4930.         Col := StringToCol(Indicator, MaxCols);
  4931.       end; {...if Indicator = (GLStringList^.Get(sColumnEntryIndicator)+' ') }
  4932.       if Col = 0 then
  4933.         ColHeadersHash.SearchName(KeyValue, Col);
  4934.       KeyColumn := Col;
  4935.     end; {...KeyColumn }
  4936.  
  4937. begin
  4938.   if not BlockOn then
  4939.   begin
  4940.     CurrBlock^.Start.Col := 1;
  4941.     CurrBlock^.Start.Row := 1;
  4942.     CurrBlock^.Stop := LastPos;
  4943.   end; {...if not BlockOn }
  4944.   Move(CurrBlock^, Block, SizeOf(CurrBlock^));
  4945.   Dialog := PDialog(GLResFile^.Get('SortDialog'));
  4946.   if Application^.ValidView(Dialog) <> nil then
  4947.   begin
  4948.     if Desktop^.ExecView(Dialog) <> cmCancel then
  4949.     begin
  4950.       Dialog^.GetData(RSortInfo);
  4951.       MessageDialog := PDialog(GLResFile^.Get('SortingDialog'));
  4952.       if Application^.ValidView(MessageDialog) <> nil then
  4953.       begin
  4954.         Desktop^.Insert(MessageDialog);
  4955.         StatusLine^.Update;
  4956.         with RSortInfo do
  4957.         begin
  4958.           SetChanged(ModifiedYes);
  4959.           SortObject^.Init(@CellHash);
  4960.           SortObject^.Sort(Block,
  4961.           KeyColumn(FirstKey), SortOrder(FirstKeyOrder),
  4962.           KeyColumn(SecondKey), SortOrder(SecondKeyOrder),
  4963.           KeyColumn(ThirdKey), SortOrder(ThirdKeyOrder));
  4964.         end; {...with RSortInfo }
  4965.         Desktop^.Delete(MessageDialog);
  4966.         Dispose(MessageDialog, Done);
  4967.         MessageDialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  4968.         Desktop^.Insert(MessageDialog);
  4969.         StoreTablesToTempFile;
  4970.         DoneHashTables;
  4971.         Pos.Col := 0;
  4972.         Pos.Row := 0;
  4973.         LoadTablesFromTempFile(Pos, 0, 0);
  4974.         Assign(F, GLStringList^.Get(sTempFileName));
  4975.         Erase(F);
  4976.         FixOverwrite;
  4977.         DisplayAllCells;
  4978.         DisplayCellData;
  4979.         Desktop^.Delete(MessageDialog);
  4980.         if MessageDialog <> nil then
  4981.           Dispose(MessageDialog, Done);
  4982.       end; {...if Application^.ValidView(MessageDialog) <> nil }
  4983.       MessageDialog := nil;
  4984.     end; {...if ExecView(Dialog) <> cmCancel }
  4985.     Dispose(Dialog, Done);
  4986.   end; {...if Application^.ValidView(Dialog) <> nil }
  4987. end; {...TSpreadSheet.SortData }
  4988.  
  4989.  
  4990. function TSpreadSheet.SortObject : PSortObject;
  4991. { Returns a pointer to the sort object to be used }
  4992. begin
  4993.   SortObject := StandardSortObject;
  4994. end; {...TSpreadSheet.SortObject }
  4995.  
  4996.  
  4997. procedure TSpreadSheet.Store(var S: TStream);
  4998. { Writes the spreadsheet object to a stream }
  4999. const
  5000.   FileHeader : String[Length(OOGridFileHeader)] = OOGridFileHeader;
  5001. begin
  5002.   TScroller.Store(S);
  5003.   S.Write(FileHeader, SizeOf(FileHeader));
  5004.   S.Write(EmptyRowsAtTop, SizeOf(EmptyRowsAtTop));
  5005.   S.Write(EmptyRowsAtBottom, SizeOf(EmptyRowsAtBottom));
  5006.   S.Write(MaxCols, Sizeof(MaxCols));
  5007.   S.Write(MaxRows, SizeOf(MaxRows));
  5008.   S.Write(DefaultColWidth, SizeOf(DefaultColWidth));
  5009.   S.Write(DefaultDecimalPlaces, SizeOf(DefaultDecimalPlaces));
  5010.   S.Write(MaxDecimalPlaces, SizeOf(MaxDecimalPlaces));
  5011.   S.Write(DefaultCurrency, SizeOf(DefaultCurrency));
  5012.   S.Write(LastPos, SizeOf(LastPos));
  5013.   StoreHashTables(S);
  5014.   S.Put(ScreenBlock);
  5015.   S.Write(CurrPos, SizeOf(CurrPos));
  5016.   S.Write(BlockOn, SizeOf(BlockOn));
  5017.   S.Put(CurrBlock);
  5018.   S.Write(DisplayFormulas, SizeOf(DisplayFormulas));
  5019.   S.Write(AutoCalc, SizeOf(AutoCalc));
  5020.   S.Write(DisplayHeaders, SizeOf(DisplayHeaders));
  5021.   S.Write(SheetProtected, SizeOf(SheetProtected));
  5022.   SetChanged(ModifiedNo);
  5023. end; {...TSpreadSheet.Store }
  5024.  
  5025.  
  5026. procedure TSpreadSheet.StoreHashTables(var S: TStream);
  5027. { Stores the hash tables in a stream }
  5028. begin
  5029.   S.Write(CellHash.Items, SizeOf(CellHash.Items));
  5030.   S.Write(WidthHash.Items, 2);
  5031.   S.Write(FormatHash.Items, SizeOf(FormatHash.Items));
  5032.   S.Write(ColHeadersHash.Items, 2);
  5033.   S.Write(UnlockedHash.Items, SizeOf(UnlockedHash.Items));
  5034.   CellHash.Store(S);
  5035.   WidthHash.Store(S);
  5036.   FormatHash.Store(S);
  5037.   ColHeadersHash.Store(S);
  5038.   UnlockedHash.Store(S);
  5039. end; {...TSpreadSheet.StoreHashTables }
  5040.  
  5041.  
  5042. procedure TSpreadSheet.StoreTablesToTempFile;
  5043. { Stores the hash tables in a temporary file in disk }
  5044. var
  5045.   S : TBufStream;
  5046. begin
  5047.   S.Init(GLStringList^.Get(sTempFileName), stCreate, 1024);
  5048.   StoreHashTables(S);
  5049.   S.Done;
  5050. end; {...TSpreadSheet.StoreTablesToTempFile }
  5051.  
  5052.  
  5053. procedure TSpreadSheet.ToggleAutoCalc;
  5054. { Turns the autocalc mode on and off }
  5055. begin
  5056.   AutoCalc := not AutoCalc;
  5057.   SetChanged(ModifiedYes);
  5058.   if AutoCalc then
  5059.     Recalc(DisplayYes);
  5060. end; {...TSpreadSheet.ToggleAutoCalc }
  5061.  
  5062.  
  5063. procedure TSpreadSheet.ToggleBlockOn;
  5064. { Turns the block state on }
  5065. begin
  5066.   if not BlockOn then
  5067.   begin
  5068.     BlockOn := True;
  5069.     CurrBlock^.Init(CurrPos);
  5070.     DisplayInfo;
  5071.   end {...if not BlockOn }
  5072. end; {...TSpreadSheet.ToggleBlockOn }
  5073.  
  5074.  
  5075. procedure TSpreadSheet.ToggleDisplayHeaders;
  5076. { Toggles between displaying and not displaying the column names }
  5077. begin
  5078.   DisplayHeaders := not DisplayHeaders;
  5079.   DisplayCols;
  5080.   SetChanged(ModifiedYes);
  5081. end; {...TSpreadSheet.ToggleDisplayHeaders }
  5082.  
  5083.  
  5084. procedure TSpreadSheet.ToggleEnd;
  5085. { Toggles on and off the Go_To_End status (the END key was pressed) }
  5086. begin
  5087.   GoToEnd := Not GoToEnd;
  5088.   DisplayInfo;
  5089. end; {...TSpreadSheet.ToggleEnd }
  5090.  
  5091.  
  5092. procedure TSpreadSheet.ToggleFormulaDisplay;
  5093. { Toggles between displaying the cell formulas or their values }
  5094. var
  5095.   OChanged : Boolean;
  5096.   CP : PCell;
  5097. begin
  5098.   Desktop^.Lock;
  5099.   DisplayFormulas := not DisplayFormulas;
  5100.   SetChanged(ModifiedYes);
  5101.   OChanged := True;
  5102.   with CellHash do
  5103.   begin
  5104.     CP := FirstItem;
  5105.     while (CP <> nil) and OChanged do
  5106.     begin
  5107.       if CP^.ShouldUpdate then
  5108.         OChanged := OverwriteHash.Change(CP, CP^.Overwritten(CellHash,
  5109.           FormatHash, WidthHash, LastPos, MaxCols, GetColWidth,
  5110.           DisplayFormulas));
  5111.         CP := NextItem;
  5112.     end; {...while (CP <> nil) and OChanged }
  5113.   end; {...with CellHash }
  5114.   DisplayAllCells;
  5115.   DisplayCellData;
  5116.   Desktop^.Unlock;
  5117. end; {...TSpreadSheet.ToggleFormulaDisplay }
  5118.  
  5119.  
  5120. function TSpreadSheet.TrackCursor: Boolean;
  5121. { Checks if the cursor is within the limits of the currently displayed
  5122.   screen block.  If not, it adjust the screen block to include
  5123.   the position of the cursor. }
  5124. begin
  5125.   TrackCursor := False;
  5126.   if CurrPos.Col < ScreenBlock^.Start.Col then
  5127.     begin
  5128.       SetScreenColStart(CurrPos.Col);
  5129.       TrackCursor := True;
  5130.     end {...if CurrPos.Col < ScreenBlock^.Start.Col }
  5131.   else if CurrPos.Col > ScreenBlock^.Stop.Col then
  5132.     begin
  5133.       SetScreenColStop(CurrPos.Col);
  5134.       TrackCursor := True;
  5135.     end; {...else if CurrPos.Col > ScreenBlock^.Stop.Col }
  5136.   if CurrPos.Row < ScreenBlock^.Start.Row then
  5137.     begin
  5138.       SetScreenRowStart(CurrPos.Row);
  5139.       TrackCursor := True;
  5140.     end {...if CurrPos.Row < ScreenBlock^.Start.Row }
  5141.   else if CurrPos.Row > ScreenBlock^.Stop.Row then
  5142.     begin
  5143.       SetScreenRowStop(CurrPos.Row);
  5144.       TrackCursor := True;
  5145.     end; {...else if CurrPos.Row > ScreenBlock^.Stop.Row }
  5146. end; {...TSpreadSheet.TrackCursor }
  5147.  
  5148.  
  5149. procedure TSpreadSheet.UpdateScreenBlockDisplay;
  5150. { Displays the screen and changes the scrollbars' values whenever the
  5151.   screen block was changed }
  5152. begin
  5153.   ExtendCurrBlock(RedrawNo);
  5154.   HScrollBar^.Value := ScreenBlock^.Start.Col;
  5155.   HScrollBar^.Drawview;
  5156.   VScrollBar^.Value := ScreenBlock^.Start.Row;
  5157.   VScrollBar^.Drawview;
  5158.   DrawView;
  5159. end; {...TSpreadSheet.UpdateScreenBlockDisplay }
  5160.  
  5161.  
  5162. function TSpreadSheet.WidthHashStart:BucketRange;
  5163. { Returns the number of initial buckets of the Width hash table }
  5164. begin
  5165.   WidthHashStart := 10;
  5166. end; {...TSpreadSheet.WidthHashStart }
  5167.  
  5168.  
  5169. function TSpreadSheet.XToCol(X: Byte): Integer;
  5170. { Returns the spreadsheet column a particular screen column position is in }
  5171. var
  5172.   ColScrPos : Byte;
  5173.   Counter   : Integer;
  5174.   Col       : Word;
  5175. begin
  5176.   Col := 0;
  5177.   with ScreenBlock^ do
  5178.   begin
  5179.     for Counter := Start.Col to Min(Succ(Stop.Col), MaxCols) do
  5180.     begin
  5181.       ColScrPos := ColToX(Counter);
  5182.       if (X < (ColScrPos + ColWidth(Counter))) and (X >= ColScrPos) then
  5183.         Col := Counter;
  5184.     end; {...for Counter }
  5185.     if (Col = 0) and (Stop.Col = MaxCols) then
  5186.       XToCol := MaxCols
  5187.     else
  5188.       XToCol := Col;
  5189.   end; {...with ScreenBlock^ }
  5190. end; {...TSpreadSheet.XToCol }
  5191.  
  5192.  
  5193. function TSpreadSheet.YToRow(Y: Byte): Integer;
  5194. { Returns the spreadsheet row a particular screen row position is in }
  5195. begin
  5196.   YToRow := ((Y - DisplayArea.UpperLeft.Row) + ScreenBlock^.Start.Row);
  5197. end; {...TSpreadSheet.YToRow }
  5198.  
  5199.  
  5200. procedure TSpreadSheet.DoneHashTables;
  5201. { Disposes all the hash tables }
  5202. var
  5203.   Block : TBlock;
  5204.   Deleted : Boolean;
  5205. begin
  5206.   Block.Init(LastPos);
  5207.   Block.Start.Col := 1;
  5208.   Block.Start.Row := 1;
  5209.   DeleteBlock(Block, Deleted);
  5210.   CellHash.Done;
  5211.   WidthHash.Done;
  5212.   FormatHash.Done;
  5213.   OverWriteHash.Done;
  5214.   ColHeadersHash.Done;
  5215.   UnlockedHash.Done;
  5216. end; {...TSpreadSheet.DoneHashTables }
  5217.  
  5218. destructor TSpreadSheet.Done;
  5219. { Disposes the spreadsheet }
  5220. begin
  5221.   if ColStart <> nil then
  5222.     FreeMem(ColStart, MaxScreenCols);
  5223.   if ScreenBlock <> nil then
  5224.     Dispose(ScreenBlock, Done);
  5225.   if CurrBlock <> nil then
  5226.     Dispose(CurrBlock, Done);
  5227.   DoneHashTables;
  5228.   TScroller.Done;
  5229. end; {...TSpreadSheet.Done }
  5230.  
  5231. begin
  5232.   ClipBoard.BlockToCopy := nil;
  5233.   InitClipBoard;
  5234.   with PrinterConfigRec do
  5235.   begin
  5236.     PrinterCondensedOnCode := DefaultPrinterCondensedOnCode;
  5237.     PrinterCondensedOffCode := DefaultPrinterCondensedOffCode;
  5238.     PrinterUnderlineOnCode := DefaultPrinterUnderlineOnCode;
  5239.     PrinterUnderlineOffCode := DefaultPrinterUnderlineOffCode;
  5240.     PrinterBoldOnCode := DefaultPrinterBoldOnCode;
  5241.     PrinterBoldOffCode := DefaultPrinterBoldOffCode;
  5242.   end; {...with PrinterConfigRec }
  5243.   with RPrint do
  5244.   begin
  5245.     PrintTo := 0;
  5246.     PrintSize := 0;
  5247.     PrintRows := 0;
  5248.     PrintColumns := 0;
  5249.     TopMargin := DefaultTopMargin;
  5250.     BottomMargin := DefaultBottomMargin;
  5251.     LeftMargin := DefaultLeftMargin;
  5252.     RightMargin := DefaultRightMargin;
  5253.     Other := 0;
  5254.     PageRows := DefaultPageRows;
  5255.     NormalCols := DefaultNormalCols;
  5256.     CondensedCols := DefaultCondensedCols;
  5257.   end; {...with RPrint }
  5258. end. {...GLTSheet unit }